From b92dccf65bab3b6b7deb79ff3321dc256eb0f53b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:03 -0500 Subject: NFS: Fix a busy inodes issue... The nfs_open_context may live longer than the file descriptor that spawned it, so it needs to carry a reference to the vfsmount. If not, then generic_shutdown_super() may end up being called before reads and writes have been flushed out. Make a couple of functions static while we're at it... Signed-off-by: Trond Myklebust --- include/linux/nfs_fs.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index b4dc6e2e10c9..1161725d75e1 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -78,6 +78,7 @@ struct nfs_access_entry { struct nfs4_state; struct nfs_open_context { atomic_t count; + struct vfsmount *vfsmnt; struct dentry *dentry; struct rpc_cred *cred; struct nfs4_state *state; @@ -311,12 +312,9 @@ extern void nfs_begin_attr_update(struct inode *); extern void nfs_end_attr_update(struct inode *); extern void nfs_begin_data_update(struct inode *); extern void nfs_end_data_update(struct inode *); -extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred); extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); extern void put_nfs_open_context(struct nfs_open_context *ctx); -extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx); extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode); -extern void nfs_file_clear_open_context(struct file *filp); /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ extern u32 root_nfs_parse_addr(char *name); /*__init*/ -- cgit v1.2.3 From 7bab377fcb495ee2e5a1cd69d235f8d84c76e3af Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:06 -0500 Subject: lockd: Don't expose the process pid to the NLM server Instead we use the nlm_lockowner->pid. Signed-off-by: Trond Myklebust --- fs/lockd/clntlock.c | 10 +++++++++- fs/lockd/clntproc.c | 7 +++++-- fs/lockd/svclock.c | 1 + fs/lockd/xdr.c | 13 ++++++++----- fs/lockd/xdr4.c | 17 ++++++++++------- include/linux/lockd/xdr.h | 1 + 6 files changed, 34 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index da6354baa0b8..8ae79ae4b998 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -125,7 +125,15 @@ u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock) list_for_each_entry(block, &nlm_blocked, b_list) { struct file_lock *fl_blocked = block->b_lock; - if (!nlm_compare_locks(fl_blocked, fl)) + if (fl_blocked->fl_start != fl->fl_start) + continue; + if (fl_blocked->fl_end != fl->fl_end) + continue; + /* + * Careful! The NLM server will return the 32-bit "pid" that + * we put on the wire: in this case the lockowner "pid". + */ + if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid) continue; if (!nlm_cmp_addr(&block->b_host->h_addr, addr)) continue; diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 615a988a92a7..acc3eb13a02b 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -132,8 +132,10 @@ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) memcpy(&lock->fh, NFS_FH(fl->fl_file->f_dentry->d_inode), sizeof(struct nfs_fh)); lock->caller = system_utsname.nodename; lock->oh.data = req->a_owner; - lock->oh.len = sprintf(req->a_owner, "%d@%s", - current->pid, system_utsname.nodename); + lock->oh.len = snprintf(req->a_owner, sizeof(req->a_owner), "%u@%s", + (unsigned int)fl->fl_u.nfs_fl.owner->pid, + system_utsname.nodename); + lock->svid = fl->fl_u.nfs_fl.owner->pid; locks_copy_lock(&lock->fl, fl); } @@ -159,6 +161,7 @@ nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) /* set default data area */ call->a_args.lock.oh.data = call->a_owner; + call->a_args.lock.svid = lock->fl.fl_pid; if (lock->oh.len > NLMCLNT_OHSIZE) { void *data = kmalloc(lock->oh.len, GFP_KERNEL); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 9cfced65d4a2..a525a141dd3b 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -397,6 +397,7 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, (long long)fl->fl_end); conflock->caller = "somehost"; /* FIXME */ conflock->oh.len = 0; /* don't return OH info */ + conflock->svid = fl->fl_pid; conflock->fl = *fl; return nlm_lck_denied; } diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index 200fbda2c6d1..1e984ab14d3f 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -131,10 +131,11 @@ nlm_decode_lock(u32 *p, struct nlm_lock *lock) || !(p = nlm_decode_fh(p, &lock->fh)) || !(p = nlm_decode_oh(p, &lock->oh))) return NULL; + lock->svid = ntohl(*p++); locks_init_lock(fl); fl->fl_owner = current->files; - fl->fl_pid = ntohl(*p++); + fl->fl_pid = (pid_t)lock->svid; fl->fl_flags = FL_POSIX; fl->fl_type = F_RDLCK; /* as good as anything else */ start = ntohl(*p++); @@ -174,7 +175,7 @@ nlm_encode_lock(u32 *p, struct nlm_lock *lock) else len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1); - *p++ = htonl(fl->fl_pid); + *p++ = htonl(lock->svid); *p++ = htonl(start); *p++ = htonl(len); @@ -197,7 +198,7 @@ nlm_encode_testres(u32 *p, struct nlm_res *resp) struct file_lock *fl = &resp->lock.fl; *p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one; - *p++ = htonl(fl->fl_pid); + *p++ = htonl(resp->lock.svid); /* Encode owner handle. */ if (!(p = xdr_encode_netobj(p, &resp->lock.oh))) @@ -298,7 +299,8 @@ nlmsvc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) memset(lock, 0, sizeof(*lock)); locks_init_lock(&lock->fl); - lock->fl.fl_pid = ~(u32) 0; + lock->svid = ~(u32) 0; + lock->fl.fl_pid = (pid_t)lock->svid; if (!(p = nlm_decode_cookie(p, &argp->cookie)) || !(p = xdr_decode_string_inplace(p, &lock->caller, @@ -415,7 +417,8 @@ nlmclt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) memset(&resp->lock, 0, sizeof(resp->lock)); locks_init_lock(fl); excl = ntohl(*p++); - fl->fl_pid = ntohl(*p++); + resp->lock.svid = ntohl(*p++); + fl->fl_pid = (pid_t)resp->lock.svid; if (!(p = nlm_decode_oh(p, &resp->lock.oh))) return -EIO; diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index fdcf105a5303..906ddc203186 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -130,10 +130,11 @@ nlm4_decode_lock(u32 *p, struct nlm_lock *lock) || !(p = nlm4_decode_fh(p, &lock->fh)) || !(p = nlm4_decode_oh(p, &lock->oh))) return NULL; + lock->svid = ntohl(*p++); locks_init_lock(fl); fl->fl_owner = current->files; - fl->fl_pid = ntohl(*p++); + fl->fl_pid = (pid_t)lock->svid; fl->fl_flags = FL_POSIX; fl->fl_type = F_RDLCK; /* as good as anything else */ p = xdr_decode_hyper(p, &start); @@ -167,7 +168,7 @@ nlm4_encode_lock(u32 *p, struct nlm_lock *lock) || (fl->fl_end > NLM4_OFFSET_MAX && fl->fl_end != OFFSET_MAX)) return NULL; - *p++ = htonl(fl->fl_pid); + *p++ = htonl(lock->svid); start = loff_t_to_s64(fl->fl_start); if (fl->fl_end == OFFSET_MAX) @@ -198,7 +199,7 @@ nlm4_encode_testres(u32 *p, struct nlm_res *resp) struct file_lock *fl = &resp->lock.fl; *p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one; - *p++ = htonl(fl->fl_pid); + *p++ = htonl(resp->lock.svid); /* Encode owner handle. */ if (!(p = xdr_encode_netobj(p, &resp->lock.oh))) @@ -212,8 +213,8 @@ nlm4_encode_testres(u32 *p, struct nlm_res *resp) p = xdr_encode_hyper(p, start); p = xdr_encode_hyper(p, len); - dprintk("xdr: encode_testres (status %d pid %d type %d start %Ld end %Ld)\n", - resp->status, fl->fl_pid, fl->fl_type, + dprintk("xdr: encode_testres (status %u pid %d type %d start %Ld end %Ld)\n", + resp->status, (int)resp->lock.svid, fl->fl_type, (long long)fl->fl_start, (long long)fl->fl_end); } @@ -303,7 +304,8 @@ nlm4svc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) memset(lock, 0, sizeof(*lock)); locks_init_lock(&lock->fl); - lock->fl.fl_pid = ~(u32) 0; + lock->svid = ~(u32) 0; + lock->fl.fl_pid = (pid_t)lock->svid; if (!(p = nlm4_decode_cookie(p, &argp->cookie)) || !(p = xdr_decode_string_inplace(p, &lock->caller, @@ -420,7 +422,8 @@ nlm4clt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) memset(&resp->lock, 0, sizeof(resp->lock)); locks_init_lock(fl); excl = ntohl(*p++); - fl->fl_pid = ntohl(*p++); + resp->lock.svid = ntohl(*p++); + fl->fl_pid = (pid_t)resp->lock.svid; if (!(p = nlm4_decode_oh(p, &resp->lock.oh))) return -EIO; diff --git a/include/linux/lockd/xdr.h b/include/linux/lockd/xdr.h index d7a5cc4cfa97..bb0a0f1caa91 100644 --- a/include/linux/lockd/xdr.h +++ b/include/linux/lockd/xdr.h @@ -28,6 +28,7 @@ struct nlm_lock { int len; /* length of "caller" */ struct nfs_fh fh; struct xdr_netobj oh; + u32 svid; struct file_lock fl; }; -- cgit v1.2.3 From 24c5d9d7ea5a64fb5f157d17aa2c67a3300f8a08 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:08 -0500 Subject: SUNRPC: Run rpci->queue_timeout on the rpciod workqueue instead of generic Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 1 + net/sunrpc/rpc_pipe.c | 5 +++-- net/sunrpc/sched.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 8b25629accd8..a390c9b8a01e 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -276,6 +276,7 @@ void rpc_show_tasks(void); #endif int rpc_init_mempool(void); void rpc_destroy_mempool(void); +extern struct workqueue_struct *rpciod_workqueue; static inline void rpc_exit(struct rpc_task *task, int status) { diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index a5c0c7b6e151..567abbe25bc8 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -91,7 +91,8 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) res = 0; } else if (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN) { if (list_empty(&rpci->pipe)) - schedule_delayed_work(&rpci->queue_timeout, + queue_delayed_work(rpciod_workqueue, + &rpci->queue_timeout, RPC_UPCALL_TIMEOUT); list_add_tail(&msg->list, &rpci->pipe); rpci->pipelen += msg->len; @@ -132,7 +133,7 @@ rpc_close_pipes(struct inode *inode) if (ops->release_pipe) ops->release_pipe(inode); cancel_delayed_work(&rpci->queue_timeout); - flush_scheduled_work(); + flush_workqueue(rpciod_workqueue); } rpc_inode_setowner(inode, NULL); mutex_unlock(&inode->i_mutex); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index e838d042f7f5..1b74420d1603 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -64,7 +64,7 @@ static LIST_HEAD(all_tasks); */ static DECLARE_MUTEX(rpciod_sema); static unsigned int rpciod_users; -static struct workqueue_struct *rpciod_workqueue; +struct workqueue_struct *rpciod_workqueue; /* * Spinlock for other critical sections of code. -- cgit v1.2.3 From 24bd68f46b1ad08d69bf32779f860df867780a7a Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Mon, 20 Mar 2006 13:44:11 -0500 Subject: NFS: Code comments update in NFS read_cache_mtime is no longer used in nfs_inode. This patch removes references of read_cache_mtime in the code comments. Signed-off-by: Goldwyn Rodrigues Signed-off-by: Trond Myklebust --- include/linux/nfs_fs.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 1161725d75e1..b71da4d4b137 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -119,8 +119,7 @@ struct nfs_inode { unsigned long cache_validity; /* bit mask */ /* - * read_cache_jiffies is when we started read-caching this inode, - * and read_cache_mtime is the mtime of the inode at that time. + * read_cache_jiffies is when we started read-caching this inode. * attrtimeo is for how long the cached information is assumed * to be valid. A successful attribute revalidation doubles * attrtimeo (up to acregmax/acdirmax), a failure resets it to @@ -129,11 +128,6 @@ struct nfs_inode { * We need to revalidate the cached attrs for this inode if * * jiffies - read_cache_jiffies > attrtimeo - * - * and invalidate any cached data/flush out any dirty pages if - * we find that - * - * mtime != read_cache_mtime */ unsigned long read_cache_jiffies; unsigned long attrtimeo; -- cgit v1.2.3 From b4629fe2f094b719847f31be1ee5ab38300038b2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:12 -0500 Subject: VFS: New /proc file /proc/self/mountstats Create a new file under /proc/self, called mountstats, where mounted file systems can export information (configuration options, performance counters, and so on). Use a mechanism similar to /proc/mounts and s_ops->show_options. This mechanism does not violate namespace security, and is safe to use while other processes are unmounting file systems. Thanks to Mike Waychison for his review and comments. Test-plan: Test concurrent mount/unmount operations while cat'ing /proc/self/mountstats. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/namespace.c | 38 ++++++++++++++++++++++++++++++++++++++ fs/proc/base.c | 39 +++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 1 + 3 files changed, 78 insertions(+) (limited to 'include') diff --git a/fs/namespace.c b/fs/namespace.c index 39c81a8d6316..71e75bcf4d28 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -399,6 +399,44 @@ struct seq_operations mounts_op = { .show = show_vfsmnt }; +static int show_vfsstat(struct seq_file *m, void *v) +{ + struct vfsmount *mnt = v; + int err = 0; + + /* device */ + if (mnt->mnt_devname) { + seq_puts(m, "device "); + mangle(m, mnt->mnt_devname); + } else + seq_puts(m, "no device"); + + /* mount point */ + seq_puts(m, " mounted on "); + seq_path(m, mnt, mnt->mnt_root, " \t\n\\"); + seq_putc(m, ' '); + + /* file system type */ + seq_puts(m, "with fstype "); + mangle(m, mnt->mnt_sb->s_type->name); + + /* optional statistics */ + if (mnt->mnt_sb->s_op->show_stats) { + seq_putc(m, ' '); + err = mnt->mnt_sb->s_op->show_stats(m, mnt); + } + + seq_putc(m, '\n'); + return err; +} + +struct seq_operations mountstats_op = { + .start = m_start, + .next = m_next, + .stop = m_stop, + .show = show_vfsstat, +}; + /** * may_umount_tree - check if a mount tree is busy * @mnt: root of mount tree diff --git a/fs/proc/base.c b/fs/proc/base.c index 20feb7568deb..8f1f49ceebec 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -104,6 +104,7 @@ enum pid_directory_inos { PROC_TGID_MAPS, PROC_TGID_NUMA_MAPS, PROC_TGID_MOUNTS, + PROC_TGID_MOUNTSTATS, PROC_TGID_WCHAN, #ifdef CONFIG_MMU PROC_TGID_SMAPS, @@ -144,6 +145,7 @@ enum pid_directory_inos { PROC_TID_MAPS, PROC_TID_NUMA_MAPS, PROC_TID_MOUNTS, + PROC_TID_MOUNTSTATS, PROC_TID_WCHAN, #ifdef CONFIG_MMU PROC_TID_SMAPS, @@ -201,6 +203,7 @@ static struct pid_entry tgid_base_stuff[] = { E(PROC_TGID_ROOT, "root", S_IFLNK|S_IRWXUGO), E(PROC_TGID_EXE, "exe", S_IFLNK|S_IRWXUGO), E(PROC_TGID_MOUNTS, "mounts", S_IFREG|S_IRUGO), + E(PROC_TGID_MOUNTSTATS, "mountstats", S_IFREG|S_IRUSR), #ifdef CONFIG_MMU E(PROC_TGID_SMAPS, "smaps", S_IFREG|S_IRUGO), #endif @@ -732,6 +735,38 @@ static struct file_operations proc_mounts_operations = { .poll = mounts_poll, }; +extern struct seq_operations mountstats_op; +static int mountstats_open(struct inode *inode, struct file *file) +{ + struct task_struct *task = proc_task(inode); + int ret = seq_open(file, &mountstats_op); + + if (!ret) { + struct seq_file *m = file->private_data; + struct namespace *namespace; + task_lock(task); + namespace = task->namespace; + if (namespace) + get_namespace(namespace); + task_unlock(task); + + if (namespace) + m->private = namespace; + else { + seq_release(inode, file); + ret = -EINVAL; + } + } + return ret; +} + +static struct file_operations proc_mountstats_operations = { + .open = mountstats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mounts_release, +}; + #define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ static ssize_t proc_info_read(struct file * file, char __user * buf, @@ -1730,6 +1765,10 @@ static struct dentry *proc_pident_lookup(struct inode *dir, inode->i_fop = &proc_smaps_operations; break; #endif + case PROC_TID_MOUNTSTATS: + case PROC_TGID_MOUNTSTATS: + inode->i_fop = &proc_mountstats_operations; + break; #ifdef CONFIG_SECURITY case PROC_TID_ATTR: inode->i_nlink = 2; diff --git a/include/linux/fs.h b/include/linux/fs.h index 128d0082522c..be21e860a9f2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1086,6 +1086,7 @@ struct super_operations { void (*umount_begin) (struct super_block *); int (*show_options)(struct seq_file *, struct vfsmount *); + int (*show_stats)(struct seq_file *, struct vfsmount *); ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t); ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t); -- cgit v1.2.3 From 7a480e250c7ca9187275d8574ae9e48a6b602cb9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:12 -0500 Subject: NFS: show retransmit settings when displaying mount options Sometimes it's important to know the exact RPC retransmit settings the kernel is using for an NFS mount point. Add this facility to the NFS client's show_options method. Test plan: Set various retransmit settings via the mount command, and check that the settings are reflected in /proc/mounts. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 8 ++++++++ include/linux/nfs_fs_sb.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 521d1dcb4cf0..48683d4bf0f6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -396,6 +396,9 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data) nfs_init_timeout_values(&timeparms, proto, data->timeo, data->retrans); + server->retrans_timeo = timeparms.to_initval; + server->retrans_count = timeparms.to_retries; + /* create transport and client */ xprt = xprt_create_proto(proto, &server->addr, &timeparms); if (IS_ERR(xprt)) { @@ -629,6 +632,8 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) proto = buf; } seq_printf(m, ",proto=%s", proto); + seq_printf(m, ",timeo=%lu", 10U * nfss->retrans_timeo / HZ); + seq_printf(m, ",retrans=%u", nfss->retrans_count); seq_puts(m, ",addr="); seq_escape(m, nfss->hostname, " \t\n\\"); return 0; @@ -1800,6 +1805,9 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, nfs_init_timeout_values(&timeparms, data->proto, data->timeo, data->retrans); + server->retrans_timeo = timeparms.to_initval; + server->retrans_count = timeparms.to_retries; + clp = nfs4_get_client(&server->addr.sin_addr); if (!clp) { dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 3d3a305488cf..a522ab97358d 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -26,6 +26,8 @@ struct nfs_server { unsigned int acregmax; unsigned int acdirmin; unsigned int acdirmax; + unsigned long retrans_timeo; /* retransmit timeout */ + unsigned int retrans_count; /* number of retransmit tries */ unsigned int namelen; char * hostname; /* remote hostname */ struct nfs_fh fh; -- cgit v1.2.3 From d9ef5a8c26aab09762afce43df64736720b4860e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:13 -0500 Subject: NFS: introduce mechanism for tracking NFS client metrics Add a per-superblock performance counter facility to the NFS client. This facility mimics the counters available for block devices and for networking. Expose these new counters via the new /proc/self/mountstats interface. Thanks to Andrew Morton and Trond Myklebust for their review and comments. Test plan: fsx and iozone on UP and SMP systems, with and without pre-emption. Watch for memory overwrite bugs, and performance loss (significantly more CPU required per op). Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 103 +++++++++++++++++++++++++++++-- fs/nfs/iostat.h | 152 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs_fs_sb.h | 3 + 3 files changed, 252 insertions(+), 6 deletions(-) create mode 100644 fs/nfs/iostat.h (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 827d69255b1b..86b756f44e27 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -42,6 +42,7 @@ #include "nfs4_fs.h" #include "callback.h" #include "delegation.h" +#include "iostat.h" #define NFSDBG_FACILITY NFSDBG_VFS #define NFS_PARANOIA 1 @@ -65,6 +66,7 @@ static void nfs_clear_inode(struct inode *); static void nfs_umount_begin(struct super_block *); static int nfs_statfs(struct super_block *, struct kstatfs *); static int nfs_show_options(struct seq_file *, struct vfsmount *); +static int nfs_show_stats(struct seq_file *, struct vfsmount *); static void nfs_zap_acl_cache(struct inode *); static struct rpc_program nfs_program; @@ -78,6 +80,7 @@ static struct super_operations nfs_sops = { .clear_inode = nfs_clear_inode, .umount_begin = nfs_umount_begin, .show_options = nfs_show_options, + .show_stats = nfs_show_stats, }; /* @@ -290,6 +293,12 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) } sb->s_root->d_op = server->rpc_ops->dentry_ops; + server->io_stats = nfs_alloc_iostats(); + if (!server->io_stats) { + no_root_error = -ENOMEM; + goto out_no_root; + } + /* Get some general file system info */ if (server->namelen == 0 && server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0) @@ -582,7 +591,7 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf) } -static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) +static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults) { static struct proc_nfs_info { int flag; @@ -598,20 +607,19 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) { 0, NULL, NULL } }; struct proc_nfs_info *nfs_infop; - struct nfs_server *nfss = NFS_SB(mnt->mnt_sb); char buf[12]; char *proto; seq_printf(m, ",vers=%d", nfss->rpc_ops->version); seq_printf(m, ",rsize=%d", nfss->rsize); seq_printf(m, ",wsize=%d", nfss->wsize); - if (nfss->acregmin != 3*HZ) + if (nfss->acregmin != 3*HZ || showdefaults) seq_printf(m, ",acregmin=%d", nfss->acregmin/HZ); - if (nfss->acregmax != 60*HZ) + if (nfss->acregmax != 60*HZ || showdefaults) seq_printf(m, ",acregmax=%d", nfss->acregmax/HZ); - if (nfss->acdirmin != 30*HZ) + if (nfss->acdirmin != 30*HZ || showdefaults) seq_printf(m, ",acdirmin=%d", nfss->acdirmin/HZ); - if (nfss->acdirmax != 60*HZ) + if (nfss->acdirmax != 60*HZ || showdefaults) seq_printf(m, ",acdirmax=%d", nfss->acdirmax/HZ); for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) { if (nfss->flags & nfs_infop->flag) @@ -633,8 +641,89 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) seq_printf(m, ",proto=%s", proto); seq_printf(m, ",timeo=%lu", 10U * nfss->retrans_timeo / HZ); seq_printf(m, ",retrans=%u", nfss->retrans_count); +} + +static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct nfs_server *nfss = NFS_SB(mnt->mnt_sb); + + nfs_show_mount_options(m, nfss, 0); + seq_puts(m, ",addr="); seq_escape(m, nfss->hostname, " \t\n\\"); + + return 0; +} + +static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) +{ + int i, cpu; + struct nfs_server *nfss = NFS_SB(mnt->mnt_sb); + struct rpc_auth *auth = nfss->client->cl_auth; + struct nfs_iostats totals = { }; + + seq_printf(m, "statvers=%s", NFS_IOSTAT_VERS); + + /* + * Display all mount option settings + */ + seq_printf(m, "\n\topts:\t"); + seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw"); + seq_puts(m, mnt->mnt_sb->s_flags & MS_SYNCHRONOUS ? ",sync" : ""); + seq_puts(m, mnt->mnt_sb->s_flags & MS_NOATIME ? ",noatime" : ""); + seq_puts(m, mnt->mnt_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : ""); + nfs_show_mount_options(m, nfss, 1); + + seq_printf(m, "\n\tcaps:\t"); + seq_printf(m, "caps=0x%x", nfss->caps); + seq_printf(m, ",wtmult=%d", nfss->wtmult); + seq_printf(m, ",dtsize=%d", nfss->dtsize); + seq_printf(m, ",bsize=%d", nfss->bsize); + seq_printf(m, ",namelen=%d", nfss->namelen); + +#ifdef CONFIG_NFS_V4 + if (nfss->rpc_ops->version == 4) { + seq_printf(m, "\n\tnfsv4:\t"); + seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]); + seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]); + seq_printf(m, ",acl=0x%x", nfss->acl_bitmask); + } +#endif + + /* + * Display security flavor in effect for this mount + */ + seq_printf(m, "\n\tsec:\tflavor=%d", auth->au_ops->au_flavor); + if (auth->au_flavor) + seq_printf(m, ",pseudoflavor=%d", auth->au_flavor); + + /* + * Display superblock I/O counters + */ + for (cpu = 0; cpu < NR_CPUS; cpu++) { + struct nfs_iostats *stats; + + if (!cpu_possible(cpu)) + continue; + + preempt_disable(); + stats = per_cpu_ptr(nfss->io_stats, cpu); + + for (i = 0; i < __NFSIOS_COUNTSMAX; i++) + totals.events[i] += stats->events[i]; + for (i = 0; i < __NFSIOS_BYTESMAX; i++) + totals.bytes[i] += stats->bytes[i]; + + preempt_enable(); + } + + seq_printf(m, "\n\tevents:\t"); + for (i = 0; i < __NFSIOS_COUNTSMAX; i++) + seq_printf(m, "%lu ", totals.events[i]); + seq_printf(m, "\n\tbytes:\t"); + for (i = 0; i < __NFSIOS_BYTESMAX; i++) + seq_printf(m, "%Lu ", totals.bytes[i]); + return 0; } @@ -1742,6 +1831,7 @@ static struct super_operations nfs4_sops = { .clear_inode = nfs4_clear_inode, .umount_begin = nfs_umount_begin, .show_options = nfs_show_options, + .show_stats = nfs_show_stats, }; /* @@ -2015,6 +2105,7 @@ out_err: out_free: kfree(server->mnt_path); kfree(server->hostname); + nfs_free_iostats(server->io_stats); kfree(server); return s; } diff --git a/fs/nfs/iostat.h b/fs/nfs/iostat.h new file mode 100644 index 000000000000..dc080e50ec57 --- /dev/null +++ b/fs/nfs/iostat.h @@ -0,0 +1,152 @@ +/* + * linux/fs/nfs/iostat.h + * + * Declarations for NFS client per-mount statistics + * + * Copyright (C) 2005, 2006 Chuck Lever + * + * NFS client per-mount statistics provide information about the health of + * the NFS client and the health of each NFS mount point. Generally these + * are not for detailed problem diagnosis, but simply to indicate that there + * is a problem. + * + * These counters are not meant to be human-readable, but are meant to be + * integrated into system monitoring tools such as "sar" and "iostat". As + * such, the counters are sampled by the tools over time, and are never + * zeroed after a file system is mounted. Moving averages can be computed + * by the tools by taking the difference between two instantaneous samples + * and dividing that by the time between the samples. + */ + +#ifndef _NFS_IOSTAT +#define _NFS_IOSTAT + +#define NFS_IOSTAT_VERS "1.0" + +/* + * NFS byte counters + * + * 1. SERVER - the number of payload bytes read from or written to the + * server by the NFS client via an NFS READ or WRITE request. + * + * 2. NORMAL - the number of bytes read or written by applications via + * the read(2) and write(2) system call interfaces. + * + * 3. DIRECT - the number of bytes read or written from files opened + * with the O_DIRECT flag. + * + * These counters give a view of the data throughput into and out of the NFS + * client. Comparing the number of bytes requested by an application with the + * number of bytes the client requests from the server can provide an + * indication of client efficiency (per-op, cache hits, etc). + * + * These counters can also help characterize which access methods are in + * use. DIRECT by itself shows whether there is any O_DIRECT traffic. + * NORMAL + DIRECT shows how much data is going through the system call + * interface. A large amount of SERVER traffic without much NORMAL or + * DIRECT traffic shows that applications are using mapped files. + * + * NFS page counters + * + * These count the number of pages read or written via nfs_readpage(), + * nfs_readpages(), or their write equivalents. + */ +enum nfs_stat_bytecounters { + NFSIOS_NORMALREADBYTES = 0, + NFSIOS_NORMALWRITTENBYTES, + NFSIOS_DIRECTREADBYTES, + NFSIOS_DIRECTWRITTENBYTES, + NFSIOS_SERVERREADBYTES, + NFSIOS_SERVERWRITTENBYTES, + NFSIOS_READPAGES, + NFSIOS_WRITEPAGES, + __NFSIOS_BYTESMAX, +}; + +/* + * NFS event counters + * + * These counters provide a low-overhead way of monitoring client activity + * without enabling NFS trace debugging. The counters show the rate at + * which VFS requests are made, and how often the client invalidates its + * data and attribute caches. This allows system administrators to monitor + * such things as how close-to-open is working, and answer questions such + * as "why are there so many GETATTR requests on the wire?" + * + * They also count anamolous events such as short reads and writes, silly + * renames due to close-after-delete, and operations that change the size + * of a file (such operations can often be the source of data corruption + * if applications aren't using file locking properly). + */ +enum nfs_stat_eventcounters { + NFSIOS_INODEREVALIDATE = 0, + NFSIOS_DENTRYREVALIDATE, + NFSIOS_DATAINVALIDATE, + NFSIOS_ATTRINVALIDATE, + NFSIOS_VFSOPEN, + NFSIOS_VFSLOOKUP, + NFSIOS_VFSACCESS, + NFSIOS_VFSUPDATEPAGE, + NFSIOS_VFSREADPAGE, + NFSIOS_VFSREADPAGES, + NFSIOS_VFSWRITEPAGE, + NFSIOS_VFSWRITEPAGES, + NFSIOS_VFSGETDENTS, + NFSIOS_VFSSETATTR, + NFSIOS_VFSFLUSH, + NFSIOS_VFSFSYNC, + NFSIOS_VFSLOCK, + NFSIOS_VFSRELEASE, + NFSIOS_CONGESTIONWAIT, + NFSIOS_SETATTRTRUNC, + NFSIOS_EXTENDWRITE, + NFSIOS_SILLYRENAME, + NFSIOS_SHORTREAD, + NFSIOS_SHORTWRITE, + __NFSIOS_COUNTSMAX, +}; + +#ifdef __KERNEL__ + +#include +#include + +struct nfs_iostats { + unsigned long long bytes[__NFSIOS_BYTESMAX]; + unsigned long events[__NFSIOS_COUNTSMAX]; +} ____cacheline_aligned; + +static inline void nfs_inc_stats(struct inode *inode, enum nfs_stat_eventcounters stat) +{ + struct nfs_iostats *iostats; + int cpu; + + cpu = get_cpu(); + iostats = per_cpu_ptr(NFS_SERVER(inode)->io_stats, cpu); + iostats->events[stat] ++; + put_cpu_no_resched(); +} + +static inline void nfs_add_stats(struct inode *inode, enum nfs_stat_bytecounters stat, unsigned long addend) +{ + struct nfs_iostats *iostats; + int cpu; + + cpu = get_cpu(); + iostats = per_cpu_ptr(NFS_SERVER(inode)->io_stats, cpu); + iostats->bytes[stat] += addend; + put_cpu_no_resched(); +} + +static inline struct nfs_iostats *nfs_alloc_iostats(void) +{ + return alloc_percpu(struct nfs_iostats); +} + +static inline void nfs_free_iostats(struct nfs_iostats *stats) +{ + free_percpu(stats); +} + +#endif +#endif diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index a522ab97358d..d65e69a06b72 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -4,6 +4,8 @@ #include #include +struct nfs_iostats; + /* * NFS client parameters stored in the superblock. */ @@ -12,6 +14,7 @@ struct nfs_server { struct rpc_clnt * client_sys; /* 2nd handle for FSINFO */ struct rpc_clnt * client_acl; /* ACL RPC client handle */ struct nfs_rpc_ops * rpc_ops; /* NFS protocol vector */ + struct nfs_iostats * io_stats; /* I/O statistics */ struct backing_dev_info backing_dev_info; int flags; /* various flags */ unsigned int caps; /* server capabilities */ -- cgit v1.2.3 From 67ec9f46b889bfb1ab0a4e307d53929d5f0692bf Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:15 -0500 Subject: NFS: report how long an NFS file system has been mounted Add a field in nfs_server to record a timestamp when a mount succeeds. Report the number of seconds the file system has been mounted via nfs_show_stats(). Test plan: Mount an NFS file system, watch the mountstats reports and compare with clock time. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 5 +++++ include/linux/nfs_fs_sb.h | 1 + 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 8ee74111e519..1b2d782374b5 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -299,6 +299,9 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) goto out_no_root; } + /* mount time stamp, in seconds */ + server->mount_time = jiffies; + /* Get some general file system info */ if (server->namelen == 0 && server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0) @@ -674,6 +677,8 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) seq_puts(m, mnt->mnt_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : ""); nfs_show_mount_options(m, nfss, 1); + seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ); + seq_printf(m, "\n\tcaps:\t"); seq_printf(m, "caps=0x%x", nfss->caps); seq_printf(m, ",wtmult=%d", nfss->wtmult); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index d65e69a06b72..65dec21af774 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -35,6 +35,7 @@ struct nfs_server { char * hostname; /* remote hostname */ struct nfs_fh fh; struct sockaddr_in addr; + unsigned long mount_time; /* when this fs was mounted */ #ifdef CONFIG_NFS_V4 /* Our own IP address, as a null-terminated string. * This is used to generate the clientid, and the callback address. -- cgit v1.2.3 From e19b63dafdf7d615b0d36b90990a07e7792b9d3a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:15 -0500 Subject: SUNRPC: track length of RPC wait queues RPC wait queue length will eventually be exported to userland via the RPC iostats interface. Test plan: Compile kernel with CONFIG_NFS enabled. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 1 + net/sunrpc/sched.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index a390c9b8a01e..6c23f73a799a 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -203,6 +203,7 @@ struct rpc_wait_queue { unsigned char priority; /* current priority */ unsigned char count; /* # task groups remaining serviced so far */ unsigned char nr; /* # tasks remaining for cookie */ + unsigned short qlen; /* total # tasks waiting in queue */ #ifdef RPC_DEBUG const char * name; #endif diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 1b74420d1603..aa0449dcd8e5 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -181,6 +181,7 @@ static void __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task * else list_add_tail(&task->u.tk_wait.list, &queue->tasks[0]); task->u.tk_wait.rpc_waitq = queue; + queue->qlen++; rpc_set_queued(task); dprintk("RPC: %4d added to queue %p \"%s\"\n", @@ -215,6 +216,7 @@ static void __rpc_remove_wait_queue(struct rpc_task *task) __rpc_remove_wait_queue_priority(task); else list_del(&task->u.tk_wait.list); + queue->qlen--; dprintk("RPC: %4d removed from queue %p \"%s\"\n", task->tk_pid, queue, rpc_qname(queue)); } -- cgit v1.2.3 From 262ca07de4d7f1bff20361c1353bb14b3607afb2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:16 -0500 Subject: SUNRPC: add a handful of per-xprt counters Monitor generic transport events. Add a transport switch callout to format transport counters for export to user-land. Test plan: Compile kernel with CONFIG_NFS enabled. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- include/linux/sunrpc/xprt.h | 13 ++++++++++++ net/sunrpc/pmap_clnt.c | 1 + net/sunrpc/xprt.c | 21 ++++++++++++++------ net/sunrpc/xprtsock.c | 48 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 6ef99b14ff09..7eebbab7160b 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -114,6 +114,7 @@ struct rpc_xprt_ops { void (*release_request)(struct rpc_task *task); void (*close)(struct rpc_xprt *xprt); void (*destroy)(struct rpc_xprt *xprt); + void (*print_stats)(struct rpc_xprt *xprt, struct seq_file *seq); }; struct rpc_xprt { @@ -187,6 +188,18 @@ struct rpc_xprt { struct list_head recv; + struct { + unsigned long bind_count, /* total number of binds */ + connect_count, /* total number of connects */ + connect_start, /* connect start timestamp */ + connect_time, /* jiffies waiting for connect */ + sends, /* how many complete requests */ + recvs, /* how many complete requests */ + bad_xids; /* lookup_rqst didn't find XID */ + + unsigned long long req_u, /* average requests on the wire */ + bklog_u; /* backlog queue utilization */ + } stat; void (*old_data_ready)(struct sock *, int); void (*old_state_change)(struct sock *); diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index 8139ce68e915..332cc5d362d5 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c @@ -82,6 +82,7 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) rpc_call_setup(child, &msg, 0); /* ... and run the child task */ + task->tk_xprt->stat.bind_count++; rpc_run_child(task, child, pmap_getport_done); return; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 8ff2c8acb223..93a0a3ca0d5f 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -548,6 +548,7 @@ void xprt_connect(struct rpc_task *task) task->tk_timeout = xprt->connect_timeout; rpc_sleep_on(&xprt->pending, task, xprt_connect_status, NULL); + xprt->stat.connect_start = jiffies; xprt->ops->connect(task); } return; @@ -558,6 +559,8 @@ static void xprt_connect_status(struct rpc_task *task) struct rpc_xprt *xprt = task->tk_xprt; if (task->tk_status >= 0) { + xprt->stat.connect_count++; + xprt->stat.connect_time += (long)jiffies - xprt->stat.connect_start; dprintk("RPC: %4d xprt_connect_status: connection established\n", task->tk_pid); return; @@ -601,16 +604,14 @@ static void xprt_connect_status(struct rpc_task *task) struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid) { struct list_head *pos; - struct rpc_rqst *req = NULL; list_for_each(pos, &xprt->recv) { struct rpc_rqst *entry = list_entry(pos, struct rpc_rqst, rq_list); - if (entry->rq_xid == xid) { - req = entry; - break; - } + if (entry->rq_xid == xid) + return entry; } - return req; + xprt->stat.bad_xids++; + return NULL; } /** @@ -646,6 +647,7 @@ void xprt_complete_rqst(struct rpc_task *task, int copied) dprintk("RPC: %5u xid %08x complete (%d bytes received)\n", task->tk_pid, ntohl(req->rq_xid), copied); + task->tk_xprt->stat.recvs++; list_del_init(&req->rq_list); req->rq_received = req->rq_private_buf.len = copied; rpc_wake_up_task(task); @@ -744,12 +746,19 @@ void xprt_transmit(struct rpc_task *task) if (status == 0) { dprintk("RPC: %4d xmit complete\n", task->tk_pid); spin_lock_bh(&xprt->transport_lock); + xprt->ops->set_retrans_timeout(task); + + xprt->stat.sends++; + xprt->stat.req_u += xprt->stat.sends - xprt->stat.recvs; + xprt->stat.bklog_u += xprt->backlog.qlen; + /* Don't race with disconnect */ if (!xprt_connected(xprt)) task->tk_status = -ENOTCONN; else if (!req->rq_received) rpc_sleep_on(&xprt->pending, task, NULL, xprt_timer); + xprt->ops->release_xprt(xprt, task); spin_unlock_bh(&xprt->transport_lock); return; diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index c458f8d1d6d1..6766b7f1ecf9 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1114,6 +1114,8 @@ static void xs_tcp_connect_worker(void *args) } /* Tell the socket layer to start connecting... */ + xprt->stat.connect_count++; + xprt->stat.connect_start = jiffies; status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr, sizeof(xprt->addr), O_NONBLOCK); dprintk("RPC: %p connect status %d connected %d sock state %d\n", @@ -1177,6 +1179,50 @@ static void xs_connect(struct rpc_task *task) } } +/** + * xs_udp_print_stats - display UDP socket-specifc stats + * @xprt: rpc_xprt struct containing statistics + * @seq: output file + * + */ +static void xs_udp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) +{ + seq_printf(seq, "\txprt:\tudp %u %lu %lu %lu %lu %Lu %Lu\n", + xprt->port, + xprt->stat.bind_count, + xprt->stat.sends, + xprt->stat.recvs, + xprt->stat.bad_xids, + xprt->stat.req_u, + xprt->stat.bklog_u); +} + +/** + * xs_tcp_print_stats - display TCP socket-specifc stats + * @xprt: rpc_xprt struct containing statistics + * @seq: output file + * + */ +static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) +{ + long idle_time = 0; + + if (xprt_connected(xprt)) + idle_time = (long)(jiffies - xprt->last_used) / HZ; + + seq_printf(seq, "\txprt:\ttcp %u %lu %lu %lu %ld %lu %lu %lu %Lu %Lu\n", + xprt->port, + xprt->stat.bind_count, + xprt->stat.connect_count, + xprt->stat.connect_time, + idle_time, + xprt->stat.sends, + xprt->stat.recvs, + xprt->stat.bad_xids, + xprt->stat.req_u, + xprt->stat.bklog_u); +} + static struct rpc_xprt_ops xs_udp_ops = { .set_buffer_size = xs_udp_set_buffer_size, .reserve_xprt = xprt_reserve_xprt_cong, @@ -1191,6 +1237,7 @@ static struct rpc_xprt_ops xs_udp_ops = { .release_request = xprt_release_rqst_cong, .close = xs_close, .destroy = xs_destroy, + .print_stats = xs_udp_print_stats, }; static struct rpc_xprt_ops xs_tcp_ops = { @@ -1204,6 +1251,7 @@ static struct rpc_xprt_ops xs_tcp_ops = { .set_retrans_timeout = xprt_set_retrans_timeout_def, .close = xs_close, .destroy = xs_destroy, + .print_stats = xs_tcp_print_stats, }; /** -- cgit v1.2.3 From ef759a2e54ed434b2f72b52a14edecd6d4eadf74 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:17 -0500 Subject: SUNRPC: introduce per-task RPC iostats Account for various things that occur while an RPC task is executed. Separate timers for RPC round trip and RPC execution time show how long RPC requests wait in queue before being sent. Eventually these will be accumulated at xprt_release time in one place where they can be viewed from userland. Test plan: Compile kernel with CONFIG_NFS enabled. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 6 ++++++ net/sunrpc/clnt.c | 2 ++ net/sunrpc/sched.c | 3 +++ net/sunrpc/xprt.c | 2 ++ net/sunrpc/xprtsock.c | 1 + 5 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 6c23f73a799a..45a64ae963ee 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -86,6 +86,12 @@ struct rpc_task { struct work_struct tk_work; /* Async task work queue */ struct rpc_wait tk_wait; /* RPC wait */ } u; + + unsigned short tk_timeouts; /* maj timeouts */ + size_t tk_bytes_sent; /* total bytes sent */ + unsigned long tk_start; /* RPC task init timestamp */ + long tk_rtt; /* round-trip time (jiffies) */ + #ifdef RPC_DEBUG unsigned short tk_pid; /* debugging aid */ #endif diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index cad7efe2cb22..84eb5b4565fc 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -996,6 +996,8 @@ call_timeout(struct rpc_task *task) } dprintk("RPC: %4d call_timeout (major)\n", task->tk_pid); + task->tk_timeouts++; + if (RPC_IS_SOFT(task)) { printk(KERN_NOTICE "%s: server %s not responding, timed out\n", clnt->cl_protname, clnt->cl_server); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index aa0449dcd8e5..cd51b5468332 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -817,6 +817,9 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, int flags, cons BUG_ON(task->tk_ops == NULL); + /* starting timestamp */ + task->tk_start = jiffies; + dprintk("RPC: %4d new task procpid %d\n", task->tk_pid, current->pid); } diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 93a0a3ca0d5f..c6241976a6ee 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -648,6 +648,8 @@ void xprt_complete_rqst(struct rpc_task *task, int copied) task->tk_pid, ntohl(req->rq_xid), copied); task->tk_xprt->stat.recvs++; + task->tk_rtt = (long)jiffies - req->rq_xtime; + list_del_init(&req->rq_list); req->rq_received = req->rq_private_buf.len = copied; rpc_wake_up_task(task); diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 6766b7f1ecf9..4b4e7dfdff14 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -382,6 +382,7 @@ static int xs_tcp_send_request(struct rpc_task *task) /* If we've sent the entire packet, immediately * reset the count of bytes sent. */ req->rq_bytes_sent += status; + task->tk_bytes_sent += status; if (likely(req->rq_bytes_sent >= req->rq_slen)) { req->rq_bytes_sent = 0; return 0; -- cgit v1.2.3 From 11c556b3d8d481829ab5f9933a25d29b00913b5a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:22 -0500 Subject: SUNRPC: provide a mechanism for collecting stats in the RPC client Add a simple mechanism for collecting stats in the RPC client. Stats are tabulated during xprt_release. Note that per_cpu shenanigans are not required here because the RPC client already serializes on the transport write lock. Test plan: Compile kernel with CONFIG_NFS enabled. Basic performance regression testing with high-speed networking and high performance server. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- include/linux/sunrpc/clnt.h | 3 +- include/linux/sunrpc/metrics.h | 77 ++++++++++++++++++++++++++++++ net/sunrpc/clnt.c | 9 ++-- net/sunrpc/stats.c | 105 +++++++++++++++++++++++++++++++++++++++++ net/sunrpc/xprt.c | 2 + 5 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 include/linux/sunrpc/metrics.h (limited to 'include') diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index f147e6b84332..0f3662002ffc 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -45,7 +45,8 @@ struct rpc_clnt { char * cl_server; /* server machine name */ char * cl_protname; /* protocol name */ struct rpc_auth * cl_auth; /* authenticator */ - struct rpc_stat * cl_stats; /* statistics */ + struct rpc_stat * cl_stats; /* per-program statistics */ + struct rpc_iostats * cl_metrics; /* per-client statistics */ unsigned int cl_softrtry : 1,/* soft timeouts */ cl_intr : 1,/* interruptible */ diff --git a/include/linux/sunrpc/metrics.h b/include/linux/sunrpc/metrics.h new file mode 100644 index 000000000000..8f96e9dc369a --- /dev/null +++ b/include/linux/sunrpc/metrics.h @@ -0,0 +1,77 @@ +/* + * linux/include/linux/sunrpc/metrics.h + * + * Declarations for RPC client per-operation metrics + * + * Copyright (C) 2005 Chuck Lever + * + * RPC client per-operation statistics provide latency and retry + * information about each type of RPC procedure in a given RPC program. + * These statistics are not for detailed problem diagnosis, but simply + * to indicate whether the problem is local or remote. + * + * These counters are not meant to be human-readable, but are meant to be + * integrated into system monitoring tools such as "sar" and "iostat". As + * such, the counters are sampled by the tools over time, and are never + * zeroed after a file system is mounted. Moving averages can be computed + * by the tools by taking the difference between two instantaneous samples + * and dividing that by the time between the samples. + * + * The counters are maintained in a single array per RPC client, indexed + * by procedure number. There is no need to maintain separate counter + * arrays per-CPU because these counters are always modified behind locks. + */ + +#ifndef _LINUX_SUNRPC_METRICS_H +#define _LINUX_SUNRPC_METRICS_H + +#include + +#define RPC_IOSTATS_VERS "1.0" + +struct rpc_iostats { + /* + * These counters give an idea about how many request + * transmissions are required, on average, to complete that + * particular procedure. Some procedures may require more + * than one transmission because the server is unresponsive, + * the client is retransmitting too aggressively, or the + * requests are large and the network is congested. + */ + unsigned long om_ops, /* count of operations */ + om_ntrans, /* count of RPC transmissions */ + om_timeouts; /* count of major timeouts */ + + /* + * These count how many bytes are sent and received for a + * given RPC procedure type. This indicates how much load a + * particular procedure is putting on the network. These + * counts include the RPC and ULP headers, and the request + * payload. + */ + unsigned long long om_bytes_sent, /* count of bytes out */ + om_bytes_recv; /* count of bytes in */ + + /* + * The length of time an RPC request waits in queue before + * transmission, the network + server latency of the request, + * and the total time the request spent from init to release + * are measured. + */ + unsigned long long om_queue, /* jiffies queued for xmit */ + om_rtt, /* jiffies for RPC RTT */ + om_execute; /* jiffies for RPC execution */ +} ____cacheline_aligned; + +struct rpc_task; +struct rpc_clnt; + +/* + * EXPORTed functions for managing rpc_iostats structures + */ +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 *); + +#endif /* _LINUX_SUNRPC_METRICS_H */ diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 84eb5b4565fc..0bb23e8e9d0c 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -28,12 +28,11 @@ #include #include #include +#include #include -#include #include - -#include +#include #define RPC_SLACK_SPACE (1024) /* total overkill */ @@ -147,6 +146,7 @@ rpc_new_client(struct rpc_xprt *xprt, char *servname, clnt->cl_vers = version->number; clnt->cl_prot = xprt->prot; clnt->cl_stats = program->stats; + clnt->cl_metrics = rpc_alloc_iostats(clnt); rpc_init_wait_queue(&clnt->cl_pmap_default.pm_bindwait, "bindwait"); if (!clnt->cl_port) @@ -245,6 +245,7 @@ rpc_clone_client(struct rpc_clnt *clnt) if (new->cl_auth) atomic_inc(&new->cl_auth->au_count); new->cl_pmap = &new->cl_pmap_default; + new->cl_metrics = rpc_alloc_iostats(clnt); rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait"); return new; out_no_clnt: @@ -315,6 +316,8 @@ rpc_destroy_client(struct rpc_clnt *clnt) if (clnt->cl_server != clnt->cl_inline_name) kfree(clnt->cl_server); out_free: + rpc_free_iostats(clnt->cl_metrics); + clnt->cl_metrics = NULL; if (clnt->cl_dentry) dput(clnt->cl_dentry); kfree(clnt); diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 4979f226e285..24ac7163b9c7 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -21,6 +21,7 @@ #include #include #include +#include #define RPCDBG_FACILITY RPCDBG_MISC @@ -106,6 +107,110 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) { } } +/** + * rpc_alloc_iostats - allocate an rpc_iostats structure + * @clnt: RPC program, version, and xprt + * + */ +struct rpc_iostats *rpc_alloc_iostats(struct rpc_clnt *clnt) +{ + unsigned int ops = clnt->cl_maxproc; + size_t size = ops * sizeof(struct rpc_iostats); + struct rpc_iostats *new; + + new = kmalloc(size, GFP_KERNEL); + if (new) + memset(new, 0 , size); + return new; +} +EXPORT_SYMBOL(rpc_alloc_iostats); + +/** + * rpc_free_iostats - release an rpc_iostats structure + * @stats: doomed rpc_iostats structure + * + */ +void rpc_free_iostats(struct rpc_iostats *stats) +{ + kfree(stats); +} +EXPORT_SYMBOL(rpc_free_iostats); + +/** + * rpc_count_iostats - tally up per-task stats + * @task: completed rpc_task + * + * Relies on the caller for serialization. + */ +void rpc_count_iostats(struct rpc_task *task) +{ + struct rpc_rqst *req = task->tk_rqstp; + struct rpc_iostats *stats = task->tk_client->cl_metrics; + struct rpc_iostats *op_metrics; + long rtt, execute, queue; + + if (!stats || !req) + return; + op_metrics = &stats[task->tk_msg.rpc_proc->p_proc]; + + op_metrics->om_ops++; + op_metrics->om_ntrans += req->rq_ntrans; + op_metrics->om_timeouts += task->tk_timeouts; + + op_metrics->om_bytes_sent += task->tk_bytes_sent; + op_metrics->om_bytes_recv += req->rq_received; + + queue = (long)req->rq_xtime - task->tk_start; + if (queue < 0) + queue = -queue; + op_metrics->om_queue += queue; + + rtt = task->tk_rtt; + if (rtt < 0) + rtt = -rtt; + op_metrics->om_rtt += rtt; + + execute = (long)jiffies - task->tk_start; + if (execute < 0) + execute = -execute; + op_metrics->om_execute += execute; +} + +#define MILLISECS_PER_JIFFY (1000UL / HZ) + +void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt) +{ + struct rpc_iostats *stats = clnt->cl_metrics; + struct rpc_xprt *xprt = clnt->cl_xprt; + unsigned int op, maxproc = clnt->cl_maxproc; + + if (!stats) + return; + + seq_printf(seq, "\tRPC iostats version: %s ", RPC_IOSTATS_VERS); + seq_printf(seq, "p/v: %u/%u (%s)\n", + clnt->cl_prog, clnt->cl_vers, clnt->cl_protname); + + if (xprt) + xprt->ops->print_stats(xprt, seq); + + seq_printf(seq, "\tper-op statistics\n"); + for (op = 0; op < maxproc; op++) { + struct rpc_iostats *metrics = &stats[op]; + seq_printf(seq, "%12u: ", op); + seq_printf(seq, "%lu %lu %lu %Lu %Lu %Lu %Lu %Lu\n", + metrics->om_ops, + metrics->om_ntrans, + metrics->om_timeouts, + metrics->om_bytes_sent, + metrics->om_bytes_recv, + metrics->om_queue * MILLISECS_PER_JIFFY, + metrics->om_rtt * MILLISECS_PER_JIFFY, + metrics->om_execute * MILLISECS_PER_JIFFY); + } +} +EXPORT_SYMBOL(rpc_print_iostats); + /* * Register/unregister RPC proc files */ diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index c6241976a6ee..eb5a262e024e 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -44,6 +44,7 @@ #include #include +#include /* * Local variables @@ -859,6 +860,7 @@ void xprt_release(struct rpc_task *task) if (!(req = task->tk_rqstp)) return; + rpc_count_iostats(task); spin_lock_bh(&xprt->transport_lock); xprt->ops->release_xprt(xprt, task); if (xprt->ops->release_request) -- cgit v1.2.3 From cc0175c1dc1de8f6af0eb0631dcc5b999a6fcc42 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:22 -0500 Subject: SUNRPC: display human-readable procedure name in rpc_iostats output Add fields to the rpc_procinfo struct that allow the display of a human-readable name for each procedure in the rpc_iostats output. Also fix it so that the NFSv4 stats are broken up correctly by sub-procedure number. NFSv4 uses only two real RPC procedures: NULL, and COMPOUND. Test plan: Mount with NFSv2, NFSv3, and NFSv4, and do "cat /proc/self/mountstats". Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/lockd/mon.c | 4 ++++ fs/lockd/xdr.c | 4 +++- fs/lockd/xdr4.c | 4 +++- fs/nfs/mount_clnt.c | 4 ++++ fs/nfs/nfs2xdr.c | 4 +++- fs/nfs/nfs3xdr.c | 6 +++++- fs/nfs/nfs4xdr.c | 2 ++ fs/nfsd/nfs4callback.c | 2 ++ include/linux/sunrpc/clnt.h | 2 ++ net/sunrpc/pmap_clnt.c | 6 ++++++ net/sunrpc/stats.c | 14 ++++++++++++-- 11 files changed, 46 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 0edc03e67966..84ee39e6a3a2 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -214,12 +214,16 @@ static struct rpc_procinfo nsm_procedures[] = { .p_encode = (kxdrproc_t) xdr_encode_mon, .p_decode = (kxdrproc_t) xdr_decode_stat_res, .p_bufsiz = MAX(SM_mon_sz, SM_monres_sz) << 2, + .p_statidx = SM_MON, + .p_name = "MONITOR", }, [SM_UNMON] = { .p_proc = SM_UNMON, .p_encode = (kxdrproc_t) xdr_encode_unmon, .p_decode = (kxdrproc_t) xdr_decode_stat, .p_bufsiz = MAX(SM_mon_id_sz, SM_unmonres_sz) << 2, + .p_statidx = SM_UNMON, + .p_name = "UNMONITOR", }, }; diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index 1e984ab14d3f..766ce06146b5 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -546,7 +546,9 @@ nlmclt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) .p_proc = NLMPROC_##proc, \ .p_encode = (kxdrproc_t) nlmclt_encode_##argtype, \ .p_decode = (kxdrproc_t) nlmclt_decode_##restype, \ - .p_bufsiz = MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2 \ + .p_bufsiz = MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2, \ + .p_statidx = NLMPROC_##proc, \ + .p_name = #proc, \ } static struct rpc_procinfo nlm_procedures[] = { diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index 906ddc203186..36eb175ec335 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -551,7 +551,9 @@ nlm4clt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) .p_proc = NLMPROC_##proc, \ .p_encode = (kxdrproc_t) nlm4clt_encode_##argtype, \ .p_decode = (kxdrproc_t) nlm4clt_decode_##restype, \ - .p_bufsiz = MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2 \ + .p_bufsiz = MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2, \ + .p_statidx = NLMPROC_##proc, \ + .p_name = #proc, \ } static struct rpc_procinfo nlm4_procedures[] = { diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index db99b8f678f8..4a1340358223 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -137,6 +137,8 @@ static struct rpc_procinfo mnt_procedures[] = { .p_encode = (kxdrproc_t) xdr_encode_dirpath, .p_decode = (kxdrproc_t) xdr_decode_fhstatus, .p_bufsiz = MNT_dirpath_sz << 2, + .p_statidx = MNTPROC_MNT, + .p_name = "MOUNT", }, }; @@ -146,6 +148,8 @@ static struct rpc_procinfo mnt3_procedures[] = { .p_encode = (kxdrproc_t) xdr_encode_dirpath, .p_decode = (kxdrproc_t) xdr_decode_fhstatus3, .p_bufsiz = MNT_dirpath_sz << 2, + .p_statidx = MOUNTPROC3_MNT, + .p_name = "MOUNT", }, }; diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 7fc0560c89c9..8cdc792ff3c7 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -682,7 +682,9 @@ nfs_stat_to_errno(int stat) .p_encode = (kxdrproc_t) nfs_xdr_##argtype, \ .p_decode = (kxdrproc_t) nfs_xdr_##restype, \ .p_bufsiz = MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2, \ - .p_timer = timer \ + .p_timer = timer, \ + .p_statidx = NFSPROC_##proc, \ + .p_name = #proc, \ } struct rpc_procinfo nfs_procedures[] = { PROC(GETATTR, fhandle, attrstat, 1), diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index b6c0b5012bce..2d8701a230f0 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -1109,7 +1109,9 @@ nfs3_xdr_setaclres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \ .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \ .p_bufsiz = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \ - .p_timer = timer \ + .p_timer = timer, \ + .p_statidx = NFS3PROC_##proc, \ + .p_name = #proc, \ } struct rpc_procinfo nfs3_procedures[] = { @@ -1150,6 +1152,7 @@ static struct rpc_procinfo nfs3_acl_procedures[] = { .p_decode = (kxdrproc_t) nfs3_xdr_getaclres, .p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2, .p_timer = 1, + .p_name = "GETACL", }, [ACLPROC3_SETACL] = { .p_proc = ACLPROC3_SETACL, @@ -1157,6 +1160,7 @@ static struct rpc_procinfo nfs3_acl_procedures[] = { .p_decode = (kxdrproc_t) nfs3_xdr_setaclres, .p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2, .p_timer = 0, + .p_name = "SETACL", }, }; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 4bbf5ef57785..b95675349ba3 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4344,6 +4344,8 @@ nfs_stat_to_errno(int stat) .p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \ .p_decode = (kxdrproc_t) nfs4_xdr_##restype, \ .p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \ + .p_statidx = NFSPROC4_CLNT_##proc, \ + .p_name = #proc, \ } struct rpc_procinfo nfs4_procedures[] = { diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index d828662d737d..4f391cbf2fd1 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -326,6 +326,8 @@ out: .p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \ .p_decode = (kxdrproc_t) nfs4_xdr_##restype, \ .p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \ + .p_statidx = NFSPROC4_CB_##call, \ + .p_name = #proc, \ } static struct rpc_procinfo nfs4_cb_procedures[] = { diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 0f3662002ffc..3bec751ee249 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -101,6 +101,8 @@ struct rpc_procinfo { unsigned int p_bufsiz; /* req. buffer size */ unsigned int p_count; /* call count */ unsigned int p_timer; /* Which RTT timer to use */ + u32 p_statidx; /* Which procedure to account */ + char * p_name; /* name of procedure */ }; #define RPC_CONGESTED(clnt) (RPCXPRT_CONGESTED((clnt)->cl_xprt)) diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index 332cc5d362d5..efa00bd9ff9e 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c @@ -261,6 +261,8 @@ static struct rpc_procinfo pmap_procedures[] = { .p_decode = (kxdrproc_t) xdr_decode_bool, .p_bufsiz = 4, .p_count = 1, + .p_statidx = PMAP_SET, + .p_name = "SET", }, [PMAP_UNSET] = { .p_proc = PMAP_UNSET, @@ -268,6 +270,8 @@ static struct rpc_procinfo pmap_procedures[] = { .p_decode = (kxdrproc_t) xdr_decode_bool, .p_bufsiz = 4, .p_count = 1, + .p_statidx = PMAP_UNSET, + .p_name = "UNSET", }, [PMAP_GETPORT] = { .p_proc = PMAP_GETPORT, @@ -275,6 +279,8 @@ static struct rpc_procinfo pmap_procedures[] = { .p_decode = (kxdrproc_t) xdr_decode_port, .p_bufsiz = 4, .p_count = 1, + .p_statidx = PMAP_GETPORT, + .p_name = "GETPORT", }, }; diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 24ac7163b9c7..53746793eca2 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -151,7 +151,7 @@ void rpc_count_iostats(struct rpc_task *task) if (!stats || !req) return; - op_metrics = &stats[task->tk_msg.rpc_proc->p_proc]; + op_metrics = &stats[task->tk_msg.rpc_proc->p_statidx]; op_metrics->om_ops++; op_metrics->om_ntrans += req->rq_ntrans; @@ -176,6 +176,16 @@ void rpc_count_iostats(struct rpc_task *task) op_metrics->om_execute += execute; } +void _print_name(struct seq_file *seq, unsigned int op, struct rpc_procinfo *procs) +{ + if (procs[op].p_name) + seq_printf(seq, "\t%12s: ", procs[op].p_name); + else if (op == 0) + seq_printf(seq, "\t NULL: "); + else + seq_printf(seq, "\t%12u: ", op); +} + #define MILLISECS_PER_JIFFY (1000UL / HZ) void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt) @@ -197,7 +207,7 @@ void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt) seq_printf(seq, "\tper-op statistics\n"); for (op = 0; op < maxproc; op++) { struct rpc_iostats *metrics = &stats[op]; - seq_printf(seq, "%12u: ", op); + _print_name(seq, op, clnt->cl_procinfo); seq_printf(seq, "%lu %lu %lu %Lu %Lu %Lu %Lu %Lu\n", metrics->om_ops, metrics->om_ntrans, -- cgit v1.2.3 From dead28da8e3fb32601d38fb32b7021122e0a3d21 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:23 -0500 Subject: SUNRPC: eliminate rpc_call() Clean-up: replace rpc_call() helper with direct call to rpc_call_sync. This makes NFSv2 and NFSv3 synchronous calls more computationally efficient, and reduces stack consumption in functions that used to invoke rpc_call more than once. Test plan: Compile kernel with CONFIG_NFS enabled. Connectathon on NFS version 2, version 3, and version 4 mount points. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/lockd/mon.c | 7 ++- fs/nfs/mount_clnt.c | 13 +++- fs/nfs/nfs3acl.c | 16 +++-- fs/nfs/nfs3proc.c | 140 +++++++++++++++++++++++++++++++------------ fs/nfs/proc.c | 107 ++++++++++++++++++++++++++------- include/linux/sunrpc/clnt.h | 14 ----- include/linux/sunrpc/sched.h | 1 - net/sunrpc/pmap_clnt.c | 34 +++++++---- 8 files changed, 237 insertions(+), 95 deletions(-) (limited to 'include') diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 84ee39e6a3a2..5dd52b70859a 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -35,6 +35,10 @@ nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res) struct rpc_clnt *clnt; int status; struct nsm_args args; + struct rpc_message msg = { + .rpc_argp = &args, + .rpc_resp = res, + }; clnt = nsm_create(); if (IS_ERR(clnt)) { @@ -49,7 +53,8 @@ nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res) args.proc = NLMPROC_NSM_NOTIFY; memset(res, 0, sizeof(*res)); - status = rpc_call(clnt, proc, &args, res, 0); + msg.rpc_proc = &clnt->cl_procinfo[proc]; + status = rpc_call_sync(clnt, &msg, 0); if (status < 0) printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n", status); diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 4a1340358223..c44d87bdddb3 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -49,9 +49,12 @@ nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, struct mnt_fhstatus result = { .fh = fh }; + struct rpc_message msg = { + .rpc_argp = path, + .rpc_resp = &result, + }; char hostname[32]; int status; - int call; dprintk("NFS: nfs_mount(%08x:%s)\n", (unsigned)ntohl(addr->sin_addr.s_addr), path); @@ -61,8 +64,12 @@ nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, if (IS_ERR(mnt_clnt)) return PTR_ERR(mnt_clnt); - call = (version == NFS_MNT3_VERSION) ? MOUNTPROC3_MNT : MNTPROC_MNT; - status = rpc_call(mnt_clnt, call, path, &result, 0); + if (version == NFS_MNT3_VERSION) + msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT]; + else + msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT]; + + status = rpc_call_sync(mnt_clnt, &msg, 0); return status < 0? status : (result.status? -EACCES : 0); } diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c index 6a5bbc0ae941..33287879bd23 100644 --- a/fs/nfs/nfs3acl.c +++ b/fs/nfs/nfs3acl.c @@ -190,6 +190,10 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) struct nfs3_getaclres res = { .fattr = &fattr, }; + struct rpc_message msg = { + .rpc_argp = &args, + .rpc_resp = &res, + }; struct posix_acl *acl; int status, count; @@ -218,8 +222,8 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) return NULL; dprintk("NFS call getacl\n"); - status = rpc_call(server->client_acl, ACLPROC3_GETACL, - &args, &res, 0); + msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_GETACL]; + status = rpc_call_sync(server->client_acl, &msg, 0); dprintk("NFS reply getacl: %d\n", status); /* pages may have been allocated at the xdr layer. */ @@ -286,6 +290,10 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, .acl_access = acl, .pages = pages, }; + struct rpc_message msg = { + .rpc_argp = &args, + .rpc_resp = &fattr, + }; int status, count; status = -EOPNOTSUPP; @@ -306,8 +314,8 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, dprintk("NFS call setacl\n"); nfs_begin_data_update(inode); - status = rpc_call(server->client_acl, ACLPROC3_SETACL, - &args, &fattr, 0); + msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL]; + status = rpc_call_sync(server->client_acl, &msg, 0); spin_lock(&inode->i_lock); NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ACCESS; spin_unlock(&inode->i_lock); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 7204ba5b2bf8..740f8b1ab04d 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -43,21 +43,7 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) return res; } -static inline int -nfs3_rpc_call_wrapper(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags) -{ - struct rpc_message msg = { - .rpc_proc = &clnt->cl_procinfo[proc], - .rpc_argp = argp, - .rpc_resp = resp, - }; - return nfs3_rpc_wrapper(clnt, &msg, flags); -} - -#define rpc_call(clnt, proc, argp, resp, flags) \ - nfs3_rpc_call_wrapper(clnt, proc, argp, resp, flags) -#define rpc_call_sync(clnt, msg, flags) \ - nfs3_rpc_wrapper(clnt, msg, flags) +#define rpc_call_sync(clnt, msg, flags) nfs3_rpc_wrapper(clnt, msg, flags) static int nfs3_async_handle_jukebox(struct rpc_task *task, struct inode *inode) @@ -75,14 +61,21 @@ static int do_proc_get_root(struct rpc_clnt *client, struct nfs_fh *fhandle, struct nfs_fsinfo *info) { + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_FSINFO], + .rpc_argp = fhandle, + .rpc_resp = info, + }; int status; dprintk("%s: call fsinfo\n", __FUNCTION__); nfs_fattr_init(info->fattr); - status = rpc_call(client, NFS3PROC_FSINFO, fhandle, info, 0); + status = rpc_call_sync(client, &msg, 0); dprintk("%s: reply fsinfo: %d\n", __FUNCTION__, status); if (!(info->fattr->valid & NFS_ATTR_FATTR)) { - status = rpc_call(client, NFS3PROC_GETATTR, fhandle, info->fattr, 0); + msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR]; + msg.rpc_resp = info->fattr; + status = rpc_call_sync(client, &msg, 0); dprintk("%s: reply getattr: %d\n", __FUNCTION__, status); } return status; @@ -110,12 +103,16 @@ static int nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR], + .rpc_argp = fhandle, + .rpc_resp = fattr, + }; int status; dprintk("NFS call getattr\n"); nfs_fattr_init(fattr); - status = rpc_call(server->client, NFS3PROC_GETATTR, - fhandle, fattr, 0); + status = rpc_call_sync(server->client, &msg, 0); dprintk("NFS reply getattr: %d\n", status); return status; } @@ -129,11 +126,16 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, .fh = NFS_FH(inode), .sattr = sattr, }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_SETATTR], + .rpc_argp = &arg, + .rpc_resp = fattr, + }; int status; dprintk("NFS call setattr\n"); nfs_fattr_init(fattr); - status = rpc_call(NFS_CLIENT(inode), NFS3PROC_SETATTR, &arg, fattr, 0); + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); if (status == 0) nfs_setattr_update_inode(inode, sattr); dprintk("NFS reply setattr: %d\n", status); @@ -155,15 +157,23 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name, .fh = fhandle, .fattr = fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_LOOKUP], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; dprintk("NFS call lookup %s\n", name->name); nfs_fattr_init(&dir_attr); nfs_fattr_init(fattr); - status = rpc_call(NFS_CLIENT(dir), NFS3PROC_LOOKUP, &arg, &res, 0); - if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR)) - status = rpc_call(NFS_CLIENT(dir), NFS3PROC_GETATTR, - fhandle, fattr, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR)) { + msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR]; + msg.rpc_argp = fhandle; + msg.rpc_resp = fattr; + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + } dprintk("NFS reply lookup: %d\n", status); if (status >= 0) status = nfs_refresh_inode(dir, &dir_attr); @@ -183,7 +193,7 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry) .rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS], .rpc_argp = &arg, .rpc_resp = &res, - .rpc_cred = entry->cred + .rpc_cred = entry->cred, }; int mode = entry->mask; int status; @@ -229,12 +239,16 @@ static int nfs3_proc_readlink(struct inode *inode, struct page *page, .pglen = pglen, .pages = &page }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_READLINK], + .rpc_argp = &args, + .rpc_resp = &fattr, + }; int status; dprintk("NFS call readlink\n"); nfs_fattr_init(&fattr); - status = rpc_call(NFS_CLIENT(inode), NFS3PROC_READLINK, - &args, &fattr, 0); + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); nfs_refresh_inode(inode, &fattr); dprintk("NFS reply readlink: %d\n", status); return status; @@ -330,6 +344,11 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, .fh = &fhandle, .fattr = &fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_CREATE], + .rpc_argp = &arg, + .rpc_resp = &res, + }; mode_t mode = sattr->ia_mode; int status; @@ -346,8 +365,8 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, again: nfs_fattr_init(&dir_attr); nfs_fattr_init(&fattr); - status = rpc_call(NFS_CLIENT(dir), NFS3PROC_CREATE, &arg, &res, 0); - nfs_post_op_update_inode(dir, &dir_attr); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + nfs_refresh_inode(dir, &dir_attr); /* If the server doesn't support the exclusive creation semantics, * try again with simple 'guarded' mode. */ @@ -477,12 +496,17 @@ nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, .fromattr = &old_dir_attr, .toattr = &new_dir_attr }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_RENAME], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name); nfs_fattr_init(&old_dir_attr); nfs_fattr_init(&new_dir_attr); - status = rpc_call(NFS_CLIENT(old_dir), NFS3PROC_RENAME, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0); nfs_post_op_update_inode(old_dir, &old_dir_attr); nfs_post_op_update_inode(new_dir, &new_dir_attr); dprintk("NFS reply rename: %d\n", status); @@ -503,12 +527,17 @@ nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) .dir_attr = &dir_attr, .fattr = &fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_LINK], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; dprintk("NFS call link %s\n", name->name); nfs_fattr_init(&dir_attr); nfs_fattr_init(&fattr); - status = rpc_call(NFS_CLIENT(inode), NFS3PROC_LINK, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); nfs_post_op_update_inode(dir, &dir_attr); nfs_post_op_update_inode(inode, &fattr); dprintk("NFS reply link: %d\n", status); @@ -534,6 +563,11 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, .fh = fhandle, .fattr = fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; if (path->len > NFS3_MAXPATHLEN) @@ -541,7 +575,7 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, dprintk("NFS call symlink %s -> %s\n", name->name, path->name); nfs_fattr_init(&dir_attr); nfs_fattr_init(fattr); - status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SYMLINK, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_post_op_update_inode(dir, &dir_attr); dprintk("NFS reply symlink: %d\n", status); return status; @@ -563,6 +597,11 @@ nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) .fh = &fhandle, .fattr = &fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_MKDIR], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int mode = sattr->ia_mode; int status; @@ -572,7 +611,7 @@ nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) nfs_fattr_init(&dir_attr); nfs_fattr_init(&fattr); - status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKDIR, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_post_op_update_inode(dir, &dir_attr); if (status != 0) goto out; @@ -594,11 +633,16 @@ nfs3_proc_rmdir(struct inode *dir, struct qstr *name) .name = name->name, .len = name->len }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_RMDIR], + .rpc_argp = &arg, + .rpc_resp = &dir_attr, + }; int status; dprintk("NFS call rmdir %s\n", name->name); nfs_fattr_init(&dir_attr); - status = rpc_call(NFS_CLIENT(dir), NFS3PROC_RMDIR, &arg, &dir_attr, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_post_op_update_inode(dir, &dir_attr); dprintk("NFS reply rmdir: %d\n", status); return status; @@ -675,6 +719,11 @@ nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, .fh = &fh, .fattr = &fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_MKNOD], + .rpc_argp = &arg, + .rpc_resp = &res, + }; mode_t mode = sattr->ia_mode; int status; @@ -693,7 +742,7 @@ nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, nfs_fattr_init(&dir_attr); nfs_fattr_init(&fattr); - status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKNOD, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_post_op_update_inode(dir, &dir_attr); if (status != 0) goto out; @@ -710,11 +759,16 @@ static int nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *stat) { + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_FSSTAT], + .rpc_argp = fhandle, + .rpc_resp = stat, + }; int status; dprintk("NFS call fsstat\n"); nfs_fattr_init(stat->fattr); - status = rpc_call(server->client, NFS3PROC_FSSTAT, fhandle, stat, 0); + status = rpc_call_sync(server->client, &msg, 0); dprintk("NFS reply statfs: %d\n", status); return status; } @@ -723,11 +777,16 @@ static int nfs3_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *info) { + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_FSINFO], + .rpc_argp = fhandle, + .rpc_resp = info, + }; int status; dprintk("NFS call fsinfo\n"); nfs_fattr_init(info->fattr); - status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0); + status = rpc_call_sync(server->client_sys, &msg, 0); dprintk("NFS reply fsinfo: %d\n", status); return status; } @@ -736,11 +795,16 @@ static int nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_pathconf *info) { + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_PATHCONF], + .rpc_argp = fhandle, + .rpc_resp = info, + }; int status; dprintk("NFS call pathconf\n"); nfs_fattr_init(info->fattr); - status = rpc_call(server->client, NFS3PROC_PATHCONF, fhandle, info, 0); + status = rpc_call_sync(server->client, &msg, 0); dprintk("NFS reply pathconf: %d\n", status); return status; } diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index f5150d71c03d..2b051ab8bea8 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -58,16 +58,23 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, { struct nfs_fattr *fattr = info->fattr; struct nfs2_fsstat fsinfo; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_GETATTR], + .rpc_argp = fhandle, + .rpc_resp = fattr, + }; int status; dprintk("%s: call getattr\n", __FUNCTION__); nfs_fattr_init(fattr); - status = rpc_call(server->client_sys, NFSPROC_GETATTR, fhandle, fattr, 0); + status = rpc_call_sync(server->client_sys, &msg, 0); dprintk("%s: reply getattr: %d\n", __FUNCTION__, status); if (status) return status; dprintk("%s: call statfs\n", __FUNCTION__); - status = rpc_call(server->client_sys, NFSPROC_STATFS, fhandle, &fsinfo, 0); + msg.rpc_proc = &nfs_procedures[NFSPROC_STATFS]; + msg.rpc_resp = &fsinfo; + status = rpc_call_sync(server->client_sys, &msg, 0); dprintk("%s: reply statfs: %d\n", __FUNCTION__, status); if (status) return status; @@ -90,12 +97,16 @@ static int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_GETATTR], + .rpc_argp = fhandle, + .rpc_resp = fattr, + }; int status; dprintk("NFS call getattr\n"); nfs_fattr_init(fattr); - status = rpc_call(server->client, NFSPROC_GETATTR, - fhandle, fattr, 0); + status = rpc_call_sync(server->client, &msg, 0); dprintk("NFS reply getattr: %d\n", status); return status; } @@ -109,6 +120,11 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, .fh = NFS_FH(inode), .sattr = sattr }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_SETATTR], + .rpc_argp = &arg, + .rpc_resp = fattr, + }; int status; /* Mask out the non-modebit related stuff from attr->ia_mode */ @@ -116,7 +132,7 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, dprintk("NFS call setattr\n"); nfs_fattr_init(fattr); - status = rpc_call(NFS_CLIENT(inode), NFSPROC_SETATTR, &arg, fattr, 0); + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); if (status == 0) nfs_setattr_update_inode(inode, sattr); dprintk("NFS reply setattr: %d\n", status); @@ -136,11 +152,16 @@ nfs_proc_lookup(struct inode *dir, struct qstr *name, .fh = fhandle, .fattr = fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_LOOKUP], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; dprintk("NFS call lookup %s\n", name->name); nfs_fattr_init(fattr); - status = rpc_call(NFS_CLIENT(dir), NFSPROC_LOOKUP, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); dprintk("NFS reply lookup: %d\n", status); return status; } @@ -154,10 +175,14 @@ static int nfs_proc_readlink(struct inode *inode, struct page *page, .pglen = pglen, .pages = &page }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_READLINK], + .rpc_argp = &args, + }; int status; dprintk("NFS call readlink\n"); - status = rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK, &args, NULL, 0); + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); dprintk("NFS reply readlink: %d\n", status); return status; } @@ -233,11 +258,16 @@ nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, .fh = &fhandle, .fattr = &fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_CREATE], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; nfs_fattr_init(&fattr); dprintk("NFS call create %s\n", dentry->d_name.name); - status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); if (status == 0) status = nfs_instantiate(dentry, &fhandle, &fattr); dprintk("NFS reply create: %d\n", status); @@ -263,6 +293,11 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, .fh = &fhandle, .fattr = &fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_CREATE], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status, mode; dprintk("NFS call mknod %s\n", dentry->d_name.name); @@ -277,13 +312,13 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, } nfs_fattr_init(&fattr); - status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); if (status == -EINVAL && S_ISFIFO(mode)) { sattr->ia_mode = mode; nfs_fattr_init(&fattr); - status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); } if (status == 0) status = nfs_instantiate(dentry, &fhandle, &fattr); @@ -302,8 +337,6 @@ nfs_proc_remove(struct inode *dir, struct qstr *name) struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_REMOVE], .rpc_argp = &arg, - .rpc_resp = NULL, - .rpc_cred = NULL }; int status; @@ -355,10 +388,14 @@ nfs_proc_rename(struct inode *old_dir, struct qstr *old_name, .toname = new_name->name, .tolen = new_name->len }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_RENAME], + .rpc_argp = &arg, + }; int status; dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name); - status = rpc_call(NFS_CLIENT(old_dir), NFSPROC_RENAME, &arg, NULL, 0); + status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0); nfs_mark_for_revalidate(old_dir); nfs_mark_for_revalidate(new_dir); dprintk("NFS reply rename: %d\n", status); @@ -374,10 +411,14 @@ nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) .toname = name->name, .tolen = name->len }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_LINK], + .rpc_argp = &arg, + }; int status; dprintk("NFS call link %s\n", name->name); - status = rpc_call(NFS_CLIENT(inode), NFSPROC_LINK, &arg, NULL, 0); + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); nfs_mark_for_revalidate(inode); nfs_mark_for_revalidate(dir); dprintk("NFS reply link: %d\n", status); @@ -397,6 +438,10 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, .tolen = path->len, .sattr = sattr }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_SYMLINK], + .rpc_argp = &arg, + }; int status; if (path->len > NFS2_MAXPATHLEN) @@ -404,7 +449,7 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, dprintk("NFS call symlink %s -> %s\n", name->name, path->name); nfs_fattr_init(fattr); fhandle->size = 0; - status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); dprintk("NFS reply symlink: %d\n", status); return status; @@ -425,11 +470,16 @@ nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) .fh = &fhandle, .fattr = &fattr }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_MKDIR], + .rpc_argp = &arg, + .rpc_resp = &res, + }; int status; dprintk("NFS call mkdir %s\n", dentry->d_name.name); nfs_fattr_init(&fattr); - status = rpc_call(NFS_CLIENT(dir), NFSPROC_MKDIR, &arg, &res, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); if (status == 0) status = nfs_instantiate(dentry, &fhandle, &fattr); @@ -445,10 +495,14 @@ nfs_proc_rmdir(struct inode *dir, struct qstr *name) .name = name->name, .len = name->len }; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_RMDIR], + .rpc_argp = &arg, + }; int status; dprintk("NFS call rmdir %s\n", name->name); - status = rpc_call(NFS_CLIENT(dir), NFSPROC_RMDIR, &arg, NULL, 0); + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); dprintk("NFS reply rmdir: %d\n", status); return status; @@ -470,13 +524,12 @@ nfs_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, .fh = NFS_FH(dir), .cookie = cookie, .count = count, - .pages = &page + .pages = &page, }; struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_READDIR], .rpc_argp = &arg, - .rpc_resp = NULL, - .rpc_cred = cred + .rpc_cred = cred, }; int status; @@ -495,11 +548,16 @@ nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *stat) { struct nfs2_fsstat fsinfo; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_STATFS], + .rpc_argp = fhandle, + .rpc_resp = &fsinfo, + }; int status; dprintk("NFS call statfs\n"); nfs_fattr_init(stat->fattr); - status = rpc_call(server->client, NFSPROC_STATFS, fhandle, &fsinfo, 0); + status = rpc_call_sync(server->client, &msg, 0); dprintk("NFS reply statfs: %d\n", status); if (status) goto out; @@ -518,11 +576,16 @@ nfs_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *info) { struct nfs2_fsstat fsinfo; + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_STATFS], + .rpc_argp = fhandle, + .rpc_resp = &fsinfo, + }; int status; dprintk("NFS call fsinfo\n"); nfs_fattr_init(info->fattr); - status = rpc_call(server->client, NFSPROC_STATFS, fhandle, &fsinfo, 0); + status = rpc_call_sync(server->client, &msg, 0); dprintk("NFS reply fsinfo: %d\n", status); if (status) goto out; diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 3bec751ee249..e37c06128e51 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -140,20 +140,6 @@ size_t rpc_max_payload(struct rpc_clnt *); void rpc_force_rebind(struct rpc_clnt *); int rpc_ping(struct rpc_clnt *clnt, int flags); -static __inline__ -int rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags) -{ - struct rpc_message msg = { - .rpc_proc = &clnt->cl_procinfo[proc], - .rpc_argp = argp, - .rpc_resp = resp, - .rpc_cred = NULL - }; - return rpc_call_sync(clnt, &msg, flags); -} - -extern void rpciod_wake_up(void); - /* * Helper function for NFSroot support */ diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 45a64ae963ee..82a91bb22362 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -276,7 +276,6 @@ void * rpc_malloc(struct rpc_task *, size_t); void rpc_free(struct rpc_task *); int rpciod_up(void); void rpciod_down(void); -void rpciod_wake_up(void); int __rpc_wait_for_completion_task(struct rpc_task *task, int (*)(void *)); #ifdef RPC_DEBUG void rpc_show_tasks(void); diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index efa00bd9ff9e..d25b054ec921 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c @@ -104,6 +104,11 @@ rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot) .pm_prot = prot, .pm_port = 0 }; + struct rpc_message msg = { + .rpc_proc = &pmap_procedures[PMAP_GETPORT], + .rpc_argp = &map, + .rpc_resp = &map.pm_port, + }; struct rpc_clnt *pmap_clnt; char hostname[32]; int status; @@ -117,7 +122,7 @@ rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot) return PTR_ERR(pmap_clnt); /* Setup the call info struct */ - status = rpc_call(pmap_clnt, PMAP_GETPORT, &map, &map.pm_port, 0); + status = rpc_call_sync(pmap_clnt, &msg, 0); if (status >= 0) { if (map.pm_port != 0) @@ -162,16 +167,27 @@ pmap_getport_done(struct rpc_task *task) int rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) { - struct sockaddr_in sin; - struct rpc_portmap map; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + struct rpc_portmap map = { + .pm_prog = prog, + .pm_vers = vers, + .pm_prot = prot, + .pm_port = port, + }; + struct rpc_message msg = { + .rpc_proc = &pmap_procedures[port ? PMAP_SET : PMAP_UNSET], + .rpc_argp = &map, + .rpc_resp = okay, + }; struct rpc_clnt *pmap_clnt; int error = 0; dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n", prog, vers, prot, port); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP, 1); if (IS_ERR(pmap_clnt)) { error = PTR_ERR(pmap_clnt); @@ -179,13 +195,7 @@ rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) return error; } - map.pm_prog = prog; - map.pm_vers = vers; - map.pm_prot = prot; - map.pm_port = port; - - error = rpc_call(pmap_clnt, port? PMAP_SET : PMAP_UNSET, - &map, okay, 0); + error = rpc_call_sync(pmap_clnt, &msg, 0); if (error < 0) { printk(KERN_WARNING -- cgit v1.2.3 From 2e0af86f618c697b44e2d67dff151256c58201c4 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Mon, 20 Mar 2006 13:44:26 -0500 Subject: locks: remove unused posix_block_lock posix_lock_file() is used to add a blocked lock to Lockd's block, so posix_block_lock() is no longer needed. Signed-off-by: Andy Adamson Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust --- fs/locks.c | 15 --------------- include/linux/fs.h | 1 - 2 files changed, 16 deletions(-) (limited to 'include') diff --git a/fs/locks.c b/fs/locks.c index d2c5306e3db0..cb940b142c5f 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1954,21 +1954,6 @@ void locks_remove_flock(struct file *filp) unlock_kernel(); } -/** - * posix_block_lock - blocks waiting for a file lock - * @blocker: the lock which is blocking - * @waiter: the lock which conflicts and has to wait - * - * lockd needs to block waiting for locks. - */ -void -posix_block_lock(struct file_lock *blocker, struct file_lock *waiter) -{ - locks_insert_block(blocker, waiter); -} - -EXPORT_SYMBOL(posix_block_lock); - /** * posix_unblock_lock - stop waiting for a file lock * @filp: how the file was opened diff --git a/include/linux/fs.h b/include/linux/fs.h index be21e860a9f2..b01482c721ae 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -757,7 +757,6 @@ extern void locks_remove_flock(struct file *); extern struct file_lock *posix_test_lock(struct file *, struct file_lock *); extern int posix_lock_file(struct file *, struct file_lock *); extern int posix_lock_file_wait(struct file *, struct file_lock *); -extern void posix_block_lock(struct file_lock *, struct file_lock *); extern int posix_unblock_lock(struct file *, struct file_lock *); extern int posix_locks_deadlock(struct file_lock *, struct file_lock *); extern int flock_lock_file_wait(struct file *filp, struct file_lock *fl); -- cgit v1.2.3 From 8dc7c3115b611c00006eac3ee5b108296432aab7 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Mon, 20 Mar 2006 13:44:26 -0500 Subject: locks,lockd: fix race in nlmsvc_testlock posix_test_lock() returns a pointer to a struct file_lock which is unprotected and can be removed while in use by the caller. Move the conflicting lock from the return to a parameter, and copy the conflicting lock. In most cases the caller ends up putting the copy of the conflicting lock on the stack. On i386, sizeof(struct file_lock) appears to be about 100 bytes. We're assuming that's reasonable. Signed-off-by: Andy Adamson Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust --- fs/lockd/svclock.c | 12 +++++------- fs/locks.c | 21 +++++++++++++-------- fs/nfs/file.c | 7 +++---- fs/nfsd/nfs4state.c | 13 ++++++------- include/linux/fs.h | 2 +- 5 files changed, 28 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index f5398097b84b..d683dd022e08 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -376,8 +376,6 @@ u32 nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, struct nlm_lock *conflock) { - struct file_lock *fl; - dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", file->f_file->f_dentry->d_inode->i_sb->s_id, file->f_file->f_dentry->d_inode->i_ino, @@ -385,14 +383,14 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); - if ((fl = posix_test_lock(file->f_file, &lock->fl)) != NULL) { + if (posix_test_lock(file->f_file, &lock->fl, &conflock->fl)) { dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n", - fl->fl_type, (long long)fl->fl_start, - (long long)fl->fl_end); + conflock->fl.fl_type, + (long long)conflock->fl.fl_start, + (long long)conflock->fl.fl_end); conflock->caller = "somehost"; /* FIXME */ conflock->oh.len = 0; /* don't return OH info */ - conflock->svid = fl->fl_pid; - conflock->fl = *fl; + conflock->svid = conflock->fl.fl_pid; return nlm_lck_denied; } diff --git a/fs/locks.c b/fs/locks.c index cb940b142c5f..231b23c12c3d 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -672,8 +672,9 @@ static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *w return result; } -struct file_lock * -posix_test_lock(struct file *filp, struct file_lock *fl) +int +posix_test_lock(struct file *filp, struct file_lock *fl, + struct file_lock *conflock) { struct file_lock *cfl; @@ -684,9 +685,13 @@ posix_test_lock(struct file *filp, struct file_lock *fl) if (posix_locks_conflict(cfl, fl)) break; } + if (cfl) { + locks_copy_lock(conflock, cfl); + unlock_kernel(); + return 1; + } unlock_kernel(); - - return (cfl); + return 0; } EXPORT_SYMBOL(posix_test_lock); @@ -1563,7 +1568,7 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) */ int fcntl_getlk(struct file *filp, struct flock __user *l) { - struct file_lock *fl, file_lock; + struct file_lock *fl, cfl, file_lock; struct flock flock; int error; @@ -1587,7 +1592,7 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) else fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { - fl = posix_test_lock(filp, &file_lock); + fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL); } flock.l_type = F_UNLCK; @@ -1717,7 +1722,7 @@ out: */ int fcntl_getlk64(struct file *filp, struct flock64 __user *l) { - struct file_lock *fl, file_lock; + struct file_lock *fl, cfl, file_lock; struct flock64 flock; int error; @@ -1741,7 +1746,7 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l) else fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { - fl = posix_test_lock(filp, &file_lock); + fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL); } flock.l_type = F_UNLCK; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 1cf07e4ad136..ee140c53dba6 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -392,15 +392,14 @@ out_swapfile: static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) { - struct file_lock *cfl; + struct file_lock cfl; struct inode *inode = filp->f_mapping->host; int status = 0; lock_kernel(); /* Try local locking first */ - cfl = posix_test_lock(filp, fl); - if (cfl != NULL) { - locks_copy_lock(fl, cfl); + if (posix_test_lock(filp, fl, &cfl)) { + locks_copy_lock(fl, &cfl); goto out; } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1143cfb64549..f6ab762bea99 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2639,7 +2639,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock struct nfs4_stateid *lock_stp; struct file *filp; struct file_lock file_lock; - struct file_lock *conflock; + struct file_lock conflock; int status = 0; unsigned int strhashval; @@ -2775,11 +2775,11 @@ conflicting_lock: /* XXX There is a race here. Future patch needed to provide * an atomic posix_lock_and_test_file */ - if (!(conflock = posix_test_lock(filp, &file_lock))) { + if (!posix_test_lock(filp, &file_lock, &conflock)) { status = nfserr_serverfault; goto out; } - nfs4_set_lock_denied(conflock, &lock->lk_denied); + nfs4_set_lock_denied(&conflock, &lock->lk_denied); out: if (status && lock->lk_is_new && lock_sop) release_stateowner(lock_sop); @@ -2800,7 +2800,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock struct inode *inode; struct file file; struct file_lock file_lock; - struct file_lock *conflicting_lock; + struct file_lock conflock; int status; if (nfs4_in_grace()) @@ -2864,10 +2864,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock file.f_dentry = current_fh->fh_dentry; status = nfs_ok; - conflicting_lock = posix_test_lock(&file, &file_lock); - if (conflicting_lock) { + if (posix_test_lock(&file, &file_lock, &conflock)) { status = nfserr_denied; - nfs4_set_lock_denied(conflicting_lock, &lockt->lt_denied); + nfs4_set_lock_denied(&conflock, &lockt->lt_denied); } out: nfs4_unlock_state(); diff --git a/include/linux/fs.h b/include/linux/fs.h index b01482c721ae..8ef4dd788a83 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -754,7 +754,7 @@ extern void locks_init_lock(struct file_lock *); extern void locks_copy_lock(struct file_lock *, struct file_lock *); extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_flock(struct file *); -extern struct file_lock *posix_test_lock(struct file *, struct file_lock *); +extern int posix_test_lock(struct file *, struct file_lock *, struct file_lock *); extern int posix_lock_file(struct file *, struct file_lock *); extern int posix_lock_file_wait(struct file *, struct file_lock *); extern int posix_unblock_lock(struct file *, struct file_lock *); -- cgit v1.2.3 From 7117bf3dfb10b534a017260d9fc643bc1d0afd2a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 20 Mar 2006 13:44:26 -0500 Subject: lockd: Remove FL_LOCKD flag Currently lockd identifies its own locks using the FL_LOCKD flag. This doesn't scale well to multiple lock managers--if we did this in nfsv4 too, for example, we'd be left with only one free flag bit. Instead, we just check whether the file manager ops (fl_lmops) set on this lock are our own. The only use for this is in nlm_traverse_locks, which uses it to find locks that need cleaning up when freeing a host or a file. In the long run it might be nice to do reference counting instead of traversing all the locks like this.... Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust --- fs/lockd/svclock.c | 2 -- fs/lockd/svcsubs.c | 2 +- include/linux/fs.h | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include') diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index d683dd022e08..d50946dcddd9 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -313,8 +313,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, /* Get existing block (in case client is busy-waiting) */ block = nlmsvc_lookup_block(file, lock, 0); - lock->fl.fl_flags |= FL_LOCKD; - again: /* Lock file against concurrent access */ down(&file->f_sema); diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 62f4a385177f..601e5b3dfe20 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -182,7 +182,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action) again: file->f_locks = 0; for (fl = inode->i_flock; fl; fl = fl->fl_next) { - if (!(fl->fl_flags & FL_LOCKD)) + if (fl->fl_lmops != &nlmsvc_lock_operations) continue; /* update current lock count */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 8ef4dd788a83..d2cffee8fc11 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -667,7 +667,6 @@ extern spinlock_t files_lock; #define FL_POSIX 1 #define FL_FLOCK 2 #define FL_ACCESS 8 /* not trying to lock, just looking */ -#define FL_LOCKD 16 /* lock held by rpc.lockd */ #define FL_LEASE 32 /* lease held on this file */ #define FL_SLEEP 128 /* A blocking lock */ -- cgit v1.2.3 From 788e7a89a03e364855583c0ab4649b94925efbb9 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:27 -0500 Subject: NFS: Cleanup of NFS write code in preparation for asynchronous o_direct This patch inverts the callback hierarchy for NFS write calls. Instead of having the NFSv2/v3/v4-specific code set up the RPC callback ops, we allow the original caller to do so. This allows for more flexibility w.r.t. how to set up and tear down the nfs_write_data structure while still allowing the NFSv3/v4 code to perform error handling. The greater flexibility is needed by the asynchronous O_DIRECT code, which wants to be able to hold on to the original nfs_write_data structures after the WRITE RPC call has completed in order to be able to replay them if the COMMIT call determines that the server has rebooted. Signed-off-by: Trond Myklebust --- fs/nfs/nfs3proc.c | 66 +++++++++-------------------------- fs/nfs/nfs4proc.c | 54 +++++++---------------------- fs/nfs/proc.c | 24 +++---------- fs/nfs/write.c | 92 +++++++++++++++++++++++++++++++++++-------------- include/linux/nfs_fs.h | 7 ---- include/linux/nfs_xdr.h | 3 +- 6 files changed, 103 insertions(+), 143 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 740f8b1ab04d..c4f7de8830e9 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -849,29 +849,17 @@ nfs3_proc_read_setup(struct nfs_read_data *data) rpc_call_setup(task, &msg, 0); } -static void nfs3_write_done(struct rpc_task *task, void *calldata) +static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data) { - struct nfs_write_data *data = calldata; - if (nfs3_async_handle_jukebox(task, data->inode)) - return; + return -EAGAIN; if (task->tk_status >= 0) nfs_post_op_update_inode(data->inode, data->res.fattr); - nfs_writeback_done(task, calldata); + return 0; } -static const struct rpc_call_ops nfs3_write_ops = { - .rpc_call_done = nfs3_write_done, - .rpc_release = nfs_writedata_release, -}; - -static void -nfs3_proc_write_setup(struct nfs_write_data *data, int how) +static void nfs3_proc_write_setup(struct nfs_write_data *data, int how) { - struct rpc_task *task = &data->task; - struct inode *inode = data->inode; - int stable; - int flags; struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_WRITE], .rpc_argp = &data->args, @@ -879,45 +867,28 @@ nfs3_proc_write_setup(struct nfs_write_data *data, int how) .rpc_cred = data->cred, }; + data->args.stable = NFS_UNSTABLE; if (how & FLUSH_STABLE) { - if (!NFS_I(inode)->ncommit) - stable = NFS_FILE_SYNC; - else - stable = NFS_DATA_SYNC; - } else - stable = NFS_UNSTABLE; - data->args.stable = stable; - - /* Set the initial flags for the task. */ - flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; + data->args.stable = NFS_FILE_SYNC; + if (NFS_I(data->inode)->ncommit) + data->args.stable = NFS_DATA_SYNC; + } /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs3_write_ops, data); - rpc_call_setup(task, &msg, 0); + rpc_call_setup(&data->task, &msg, 0); } -static void nfs3_commit_done(struct rpc_task *task, void *calldata) +static int nfs3_commit_done(struct rpc_task *task, struct nfs_write_data *data) { - struct nfs_write_data *data = calldata; - if (nfs3_async_handle_jukebox(task, data->inode)) - return; + return -EAGAIN; if (task->tk_status >= 0) nfs_post_op_update_inode(data->inode, data->res.fattr); - nfs_commit_done(task, calldata); + return 0; } -static const struct rpc_call_ops nfs3_commit_ops = { - .rpc_call_done = nfs3_commit_done, - .rpc_release = nfs_commit_release, -}; - -static void -nfs3_proc_commit_setup(struct nfs_write_data *data, int how) +static void nfs3_proc_commit_setup(struct nfs_write_data *data, int how) { - struct rpc_task *task = &data->task; - struct inode *inode = data->inode; - int flags; struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT], .rpc_argp = &data->args, @@ -925,12 +896,7 @@ nfs3_proc_commit_setup(struct nfs_write_data *data, int how) .rpc_cred = data->cred, }; - /* Set the initial flags for the task. */ - flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; - - /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs3_commit_ops, data); - rpc_call_setup(task, &msg, 0); + rpc_call_setup(&data->task, &msg, 0); } static int @@ -970,7 +936,9 @@ struct nfs_rpc_ops nfs_v3_clientops = { .decode_dirent = nfs3_decode_dirent, .read_setup = nfs3_proc_read_setup, .write_setup = nfs3_proc_write_setup, + .write_done = nfs3_write_done, .commit_setup = nfs3_proc_commit_setup, + .commit_done = nfs3_commit_done, .file_open = nfs_open, .file_release = nfs_release, .lock = nfs3_proc_lock, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f1ff4fa6cce5..ef4dc315ecc2 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2388,32 +2388,23 @@ nfs4_proc_read_setup(struct nfs_read_data *data) rpc_call_setup(task, &msg, 0); } -static void nfs4_write_done(struct rpc_task *task, void *calldata) +static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) { - struct nfs_write_data *data = calldata; struct inode *inode = data->inode; if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { rpc_restart_call(task); - return; + return -EAGAIN; } if (task->tk_status >= 0) { renew_lease(NFS_SERVER(inode), data->timestamp); nfs_post_op_update_inode(inode, data->res.fattr); } - /* Call back common NFS writeback processing */ - nfs_writeback_done(task, calldata); + return 0; } -static const struct rpc_call_ops nfs4_write_ops = { - .rpc_call_done = nfs4_write_done, - .rpc_release = nfs_writedata_release, -}; - -static void -nfs4_proc_write_setup(struct nfs_write_data *data, int how) +static void nfs4_proc_write_setup(struct nfs_write_data *data, int how) { - struct rpc_task *task = &data->task; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE], .rpc_argp = &data->args, @@ -2423,7 +2414,6 @@ nfs4_proc_write_setup(struct nfs_write_data *data, int how) struct inode *inode = data->inode; struct nfs_server *server = NFS_SERVER(inode); int stable; - int flags; if (how & FLUSH_STABLE) { if (!NFS_I(inode)->ncommit) @@ -2438,57 +2428,37 @@ nfs4_proc_write_setup(struct nfs_write_data *data, int how) data->timestamp = jiffies; - /* Set the initial flags for the task. */ - flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; - /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_write_ops, data); - rpc_call_setup(task, &msg, 0); + rpc_call_setup(&data->task, &msg, 0); } -static void nfs4_commit_done(struct rpc_task *task, void *calldata) +static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data) { - struct nfs_write_data *data = calldata; struct inode *inode = data->inode; if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { rpc_restart_call(task); - return; + return -EAGAIN; } if (task->tk_status >= 0) nfs_post_op_update_inode(inode, data->res.fattr); - /* Call back common NFS writeback processing */ - nfs_commit_done(task, calldata); + return 0; } -static const struct rpc_call_ops nfs4_commit_ops = { - .rpc_call_done = nfs4_commit_done, - .rpc_release = nfs_commit_release, -}; - -static void -nfs4_proc_commit_setup(struct nfs_write_data *data, int how) +static void nfs4_proc_commit_setup(struct nfs_write_data *data, int how) { - struct rpc_task *task = &data->task; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT], .rpc_argp = &data->args, .rpc_resp = &data->res, .rpc_cred = data->cred, }; - struct inode *inode = data->inode; - struct nfs_server *server = NFS_SERVER(inode); - int flags; + struct nfs_server *server = NFS_SERVER(data->inode); data->args.bitmask = server->attr_bitmask; data->res.server = server; - /* Set the initial flags for the task. */ - flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; - - /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_commit_ops, data); - rpc_call_setup(task, &msg, 0); + rpc_call_setup(&data->task, &msg, 0); } /* @@ -3648,7 +3618,9 @@ struct nfs_rpc_ops nfs_v4_clientops = { .decode_dirent = nfs4_decode_dirent, .read_setup = nfs4_proc_read_setup, .write_setup = nfs4_proc_write_setup, + .write_done = nfs4_write_done, .commit_setup = nfs4_proc_commit_setup, + .commit_done = nfs4_commit_done, .file_open = nfs_open, .file_release = nfs_release, .lock = nfs4_proc_lock, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 2b051ab8bea8..608aa5932a1d 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -654,26 +654,15 @@ nfs_proc_read_setup(struct nfs_read_data *data) rpc_call_setup(task, &msg, 0); } -static void nfs_write_done(struct rpc_task *task, void *calldata) +static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data) { - struct nfs_write_data *data = calldata; - if (task->tk_status >= 0) nfs_post_op_update_inode(data->inode, data->res.fattr); - nfs_writeback_done(task, calldata); + return 0; } -static const struct rpc_call_ops nfs_write_ops = { - .rpc_call_done = nfs_write_done, - .rpc_release = nfs_writedata_release, -}; - -static void -nfs_proc_write_setup(struct nfs_write_data *data, int how) +static void nfs_proc_write_setup(struct nfs_write_data *data, int how) { - struct rpc_task *task = &data->task; - struct inode *inode = data->inode; - int flags; struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_WRITE], .rpc_argp = &data->args, @@ -684,12 +673,8 @@ nfs_proc_write_setup(struct nfs_write_data *data, int how) /* Note: NFSv2 ignores @stable and always uses NFS_FILE_SYNC */ data->args.stable = NFS_FILE_SYNC; - /* Set the initial flags for the task. */ - flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; - /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs_write_ops, data); - rpc_call_setup(task, &msg, 0); + rpc_call_setup(&data->task, &msg, 0); } static void @@ -736,6 +721,7 @@ struct nfs_rpc_ops nfs_v2_clientops = { .decode_dirent = nfs_decode_dirent, .read_setup = nfs_proc_read_setup, .write_setup = nfs_proc_write_setup, + .write_done = nfs_write_done, .commit_setup = nfs_proc_commit_setup, .file_open = nfs_open, .file_release = nfs_release, diff --git a/fs/nfs/write.c b/fs/nfs/write.c index e7c8361cf201..5912274ff1a1 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -77,12 +77,14 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context*, struct inode *, struct page *, unsigned int, unsigned int); -static void nfs_writeback_done_partial(struct nfs_write_data *, int); -static void nfs_writeback_done_full(struct nfs_write_data *, int); +static int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *); static int nfs_wait_on_write_congestion(struct address_space *, int); static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int); static int nfs_flush_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how); +static const struct rpc_call_ops nfs_write_partial_ops; +static const struct rpc_call_ops nfs_write_full_ops; +static const struct rpc_call_ops nfs_commit_ops; static kmem_cache_t *nfs_wdata_cachep; mempool_t *nfs_wdata_mempool; @@ -872,10 +874,12 @@ static inline int flush_task_priority(int how) */ static void nfs_write_rpcsetup(struct nfs_page *req, struct nfs_write_data *data, + const struct rpc_call_ops *call_ops, unsigned int count, unsigned int offset, int how) { struct inode *inode; + int flags; /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ @@ -896,6 +900,9 @@ static void nfs_write_rpcsetup(struct nfs_page *req, data->res.verf = &data->verf; nfs_fattr_init(&data->fattr); + /* Set up the initial task struct. */ + flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; + rpc_init_task(&data->task, NFS_CLIENT(inode), flags, call_ops, data); NFS_PROTO(inode)->write_setup(data, how); data->task.tk_priority = flush_task_priority(how); @@ -959,14 +966,15 @@ static int nfs_flush_multi(struct list_head *head, struct inode *inode, int how) list_del_init(&data->pages); data->pagevec[0] = page; - data->complete = nfs_writeback_done_partial; if (nbytes > wsize) { - nfs_write_rpcsetup(req, data, wsize, offset, how); + nfs_write_rpcsetup(req, data, &nfs_write_partial_ops, + wsize, offset, how); offset += wsize; nbytes -= wsize; } else { - nfs_write_rpcsetup(req, data, nbytes, offset, how); + nfs_write_rpcsetup(req, data, &nfs_write_partial_ops, + nbytes, offset, how); nbytes = 0; } nfs_execute_write(data); @@ -1020,9 +1028,8 @@ static int nfs_flush_one(struct list_head *head, struct inode *inode, int how) } req = nfs_list_entry(data->pages.next); - data->complete = nfs_writeback_done_full; /* Set up the argument struct */ - nfs_write_rpcsetup(req, data, count, 0, how); + nfs_write_rpcsetup(req, data, &nfs_write_full_ops, count, 0, how); nfs_execute_write(data); return 0; @@ -1066,8 +1073,9 @@ nfs_flush_list(struct list_head *head, int wpages, int how) /* * Handle a write reply that flushed part of a page. */ -static void nfs_writeback_done_partial(struct nfs_write_data *data, int status) +static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) { + struct nfs_write_data *data = calldata; struct nfs_page *req = data->req; struct page *page = req->wb_page; @@ -1077,11 +1085,14 @@ static void nfs_writeback_done_partial(struct nfs_write_data *data, int status) req->wb_bytes, (long long)req_offset(req)); - if (status < 0) { + if (nfs_writeback_done(task, data) != 0) + return; + + if (task->tk_status < 0) { ClearPageUptodate(page); SetPageError(page); - req->wb_context->error = status; - dprintk(", error = %d\n", status); + req->wb_context->error = task->tk_status; + dprintk(", error = %d\n", task->tk_status); } else { #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) if (data->verf.committed < NFS_FILE_SYNC) { @@ -1102,6 +1113,11 @@ static void nfs_writeback_done_partial(struct nfs_write_data *data, int status) nfs_writepage_release(req); } +static const struct rpc_call_ops nfs_write_partial_ops = { + .rpc_call_done = nfs_writeback_done_partial, + .rpc_release = nfs_writedata_release, +}; + /* * Handle a write reply that flushes a whole page. * @@ -1109,11 +1125,15 @@ static void nfs_writeback_done_partial(struct nfs_write_data *data, int status) * writebacks since the page->count is kept > 1 for as long * as the page has a write request pending. */ -static void nfs_writeback_done_full(struct nfs_write_data *data, int status) +static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) { + struct nfs_write_data *data = calldata; struct nfs_page *req; struct page *page; + if (nfs_writeback_done(task, data) != 0) + return; + /* Update attributes as result of writeback. */ while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); @@ -1126,13 +1146,13 @@ static void nfs_writeback_done_full(struct nfs_write_data *data, int status) req->wb_bytes, (long long)req_offset(req)); - if (status < 0) { + if (task->tk_status < 0) { ClearPageUptodate(page); SetPageError(page); - req->wb_context->error = status; + req->wb_context->error = task->tk_status; end_page_writeback(page); nfs_inode_remove_request(req); - dprintk(", error = %d\n", status); + dprintk(", error = %d\n", task->tk_status); goto next; } end_page_writeback(page); @@ -1154,18 +1174,28 @@ static void nfs_writeback_done_full(struct nfs_write_data *data, int status) } } +static const struct rpc_call_ops nfs_write_full_ops = { + .rpc_call_done = nfs_writeback_done_full, + .rpc_release = nfs_writedata_release, +}; + + /* * This function is called when the WRITE call is complete. */ -void nfs_writeback_done(struct rpc_task *task, void *calldata) +static int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) { - struct nfs_write_data *data = calldata; struct nfs_writeargs *argp = &data->args; struct nfs_writeres *resp = &data->res; + int status; dprintk("NFS: %4d nfs_writeback_done (status %d)\n", task->tk_pid, task->tk_status); + /* Call the NFS version-specific code */ + status = NFS_PROTO(data->inode)->write_done(task, data); + if (status != 0) + return status; nfs_add_stats(data->inode, NFSIOS_SERVERWRITTENBYTES, resp->count); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) @@ -1210,7 +1240,7 @@ void nfs_writeback_done(struct rpc_task *task, void *calldata) argp->stable = NFS_FILE_SYNC; } rpc_restart_call(task); - return; + return -EAGAIN; } if (time_before(complain, jiffies)) { printk(KERN_WARNING @@ -1221,11 +1251,7 @@ void nfs_writeback_done(struct rpc_task *task, void *calldata) /* Can't do anything about it except throw an error. */ task->tk_status = -EIO; } - - /* - * Process the nfs_page list - */ - data->complete(data, task->tk_status); + return 0; } @@ -1239,10 +1265,12 @@ void nfs_commit_release(void *wdata) * Set up the argument/result storage required for the RPC call. */ static void nfs_commit_rpcsetup(struct list_head *head, - struct nfs_write_data *data, int how) + struct nfs_write_data *data, + int how) { struct nfs_page *first; struct inode *inode; + int flags; /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ @@ -1262,7 +1290,10 @@ static void nfs_commit_rpcsetup(struct list_head *head, data->res.fattr = &data->fattr; data->res.verf = &data->verf; nfs_fattr_init(&data->fattr); - + + /* Set up the initial task struct. */ + flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; + rpc_init_task(&data->task, NFS_CLIENT(inode), flags, &nfs_commit_ops, data); NFS_PROTO(inode)->commit_setup(data, how); data->task.tk_priority = flush_task_priority(how); @@ -1303,7 +1334,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how) /* * COMMIT call returned */ -void nfs_commit_done(struct rpc_task *task, void *calldata) +static void nfs_commit_done(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; struct nfs_page *req; @@ -1312,6 +1343,10 @@ void nfs_commit_done(struct rpc_task *task, void *calldata) dprintk("NFS: %4d nfs_commit_done (status %d)\n", task->tk_pid, task->tk_status); + /* Call the NFS version-specific code */ + if (NFS_PROTO(data->inode)->commit_done(task, data) != 0) + return; + while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); @@ -1345,6 +1380,11 @@ void nfs_commit_done(struct rpc_task *task, void *calldata) } sub_page_state(nr_unstable,res); } + +static const struct rpc_call_ops nfs_commit_ops = { + .rpc_call_done = nfs_commit_done, + .rpc_release = nfs_commit_release, +}; #endif static int nfs_flush_inode(struct inode *inode, unsigned long idx_start, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index b71da4d4b137..782e59765696 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -407,13 +407,6 @@ extern int nfs_writepage(struct page *page, struct writeback_control *wbc); extern int nfs_writepages(struct address_space *, struct writeback_control *); extern int nfs_flush_incompatible(struct file *file, struct page *page); extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int); -extern void nfs_writeback_done(struct rpc_task *task, void *data); -extern void nfs_writedata_release(void *data); - -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -extern void nfs_commit_done(struct rpc_task *, void *data); -extern void nfs_commit_release(void *data); -#endif /* * Try to write back everything synchronously (but check the diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 6d6f69ec5675..277750cc70c0 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -714,7 +714,6 @@ struct nfs_write_data { #ifdef CONFIG_NFS_V4 unsigned long timestamp; /* For lease renewal */ #endif - void (*complete) (struct nfs_write_data *, int); struct page *page_array[NFS_PAGEVEC_SIZE + 1]; }; @@ -770,7 +769,9 @@ struct nfs_rpc_ops { u32 * (*decode_dirent)(u32 *, struct nfs_entry *, int plus); void (*read_setup) (struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, int how); + int (*write_done) (struct rpc_task *, struct nfs_write_data *); void (*commit_setup) (struct nfs_write_data *, int how); + int (*commit_done) (struct rpc_task *, struct nfs_write_data *); int (*file_open) (struct inode *, struct file *); int (*file_release) (struct inode *, struct file *); int (*lock)(struct file *, int, struct file_lock *); -- cgit v1.2.3 From ec06c096edec0755534c7126f4caded69de131c2 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:27 -0500 Subject: NFS: Cleanup of NFS read code Same callback hierarchy inversion as for the NFS write calls. This patch is not strictly speaking needed by the O_DIRECT code, but avoids confusing differences between the asynchronous read and write code. Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 17 +++++++++++---- fs/nfs/nfs3proc.c | 27 +++++------------------ fs/nfs/nfs4proc.c | 33 ++++++++-------------------- fs/nfs/proc.c | 25 +++++---------------- fs/nfs/read.c | 58 ++++++++++++++++++++++++++++++++++--------------- include/linux/nfs_fs.h | 4 ++-- include/linux/nfs_xdr.h | 2 +- 7 files changed, 77 insertions(+), 89 deletions(-) (limited to 'include') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index fc07ce4885da..3f87a72bd137 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -218,14 +218,17 @@ static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, unsigned int * until the RPCs complete. This could be long *after* we are woken up in * nfs_direct_read_wait (for instance, if someone hits ^C on a slow server). */ -static void nfs_direct_read_result(struct nfs_read_data *data, int status) +static void nfs_direct_read_result(struct rpc_task *task, void *calldata) { + struct nfs_read_data *data = calldata; struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; - if (likely(status >= 0)) + if (nfs_readpage_result(task, data) != 0) + return; + if (likely(task->tk_status >= 0)) atomic_add(data->res.count, &dreq->count); else - atomic_set(&dreq->error, status); + atomic_set(&dreq->error, task->tk_status); if (unlikely(atomic_dec_and_test(&dreq->complete))) { nfs_free_user_pages(dreq->pages, dreq->npages, 1); @@ -234,6 +237,11 @@ static void nfs_direct_read_result(struct nfs_read_data *data, int status) } } +static const struct rpc_call_ops nfs_read_direct_ops = { + .rpc_call_done = nfs_direct_read_result, + .rpc_release = nfs_readdata_release, +}; + /** * nfs_direct_read_schedule - dispatch NFS READ operations for a direct read * @dreq: address of nfs_direct_req struct for this request @@ -280,10 +288,11 @@ static void nfs_direct_read_schedule(struct nfs_direct_req *dreq, data->res.eof = 0; data->res.count = bytes; + rpc_init_task(&data->task, NFS_CLIENT(inode), RPC_TASK_ASYNC, + &nfs_read_direct_ops, data); NFS_PROTO(inode)->read_setup(data); data->task.tk_cookie = (unsigned long) inode; - data->complete = nfs_direct_read_result; lock_kernel(); rpc_execute(&data->task); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index c4f7de8830e9..cf186f0d2b3b 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -811,29 +811,18 @@ nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int); -static void nfs3_read_done(struct rpc_task *task, void *calldata) +static int nfs3_read_done(struct rpc_task *task, struct nfs_read_data *data) { - struct nfs_read_data *data = calldata; - if (nfs3_async_handle_jukebox(task, data->inode)) - return; + return -EAGAIN; /* Call back common NFS readpage processing */ if (task->tk_status >= 0) nfs_refresh_inode(data->inode, &data->fattr); - nfs_readpage_result(task, calldata); + return 0; } -static const struct rpc_call_ops nfs3_read_ops = { - .rpc_call_done = nfs3_read_done, - .rpc_release = nfs_readdata_release, -}; - -static void -nfs3_proc_read_setup(struct nfs_read_data *data) +static void nfs3_proc_read_setup(struct nfs_read_data *data) { - struct rpc_task *task = &data->task; - struct inode *inode = data->inode; - int flags; struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_READ], .rpc_argp = &data->args, @@ -841,12 +830,7 @@ nfs3_proc_read_setup(struct nfs_read_data *data) .rpc_cred = data->cred, }; - /* N.B. Do we need to test? Never called for swapfile inode */ - flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); - - /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs3_read_ops, data); - rpc_call_setup(task, &msg, 0); + rpc_call_setup(&data->task, &msg, 0); } static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data) @@ -935,6 +919,7 @@ struct nfs_rpc_ops nfs_v3_clientops = { .pathconf = nfs3_proc_pathconf, .decode_dirent = nfs3_decode_dirent, .read_setup = nfs3_proc_read_setup, + .read_done = nfs3_read_done, .write_setup = nfs3_proc_write_setup, .write_done = nfs3_write_done, .commit_setup = nfs3_proc_commit_setup, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index ef4dc315ecc2..bad1eae5608a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2345,47 +2345,31 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, return err; } -static void nfs4_read_done(struct rpc_task *task, void *calldata) +static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) { - struct nfs_read_data *data = calldata; - struct inode *inode = data->inode; + struct nfs_server *server = NFS_SERVER(data->inode); - if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { + if (nfs4_async_handle_error(task, server) == -EAGAIN) { rpc_restart_call(task); - return; + return -EAGAIN; } if (task->tk_status > 0) - renew_lease(NFS_SERVER(inode), data->timestamp); - /* Call back common NFS readpage processing */ - nfs_readpage_result(task, calldata); + renew_lease(server, data->timestamp); + return 0; } -static const struct rpc_call_ops nfs4_read_ops = { - .rpc_call_done = nfs4_read_done, - .rpc_release = nfs_readdata_release, -}; - -static void -nfs4_proc_read_setup(struct nfs_read_data *data) +static void nfs4_proc_read_setup(struct nfs_read_data *data) { - struct rpc_task *task = &data->task; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ], .rpc_argp = &data->args, .rpc_resp = &data->res, .rpc_cred = data->cred, }; - struct inode *inode = data->inode; - int flags; data->timestamp = jiffies; - /* N.B. Do we need to test? Never called for swapfile inode */ - flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); - - /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_read_ops, data); - rpc_call_setup(task, &msg, 0); + rpc_call_setup(&data->task, &msg, 0); } static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) @@ -3617,6 +3601,7 @@ struct nfs_rpc_ops nfs_v4_clientops = { .pathconf = nfs4_proc_pathconf, .decode_dirent = nfs4_decode_dirent, .read_setup = nfs4_proc_read_setup, + .read_done = nfs4_read_done, .write_setup = nfs4_proc_write_setup, .write_done = nfs4_write_done, .commit_setup = nfs4_proc_commit_setup, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 608aa5932a1d..9dd85cac2df0 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -613,10 +613,8 @@ nfs_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int); -static void nfs_read_done(struct rpc_task *task, void *calldata) +static int nfs_read_done(struct rpc_task *task, struct nfs_read_data *data) { - struct nfs_read_data *data = calldata; - if (task->tk_status >= 0) { nfs_refresh_inode(data->inode, data->res.fattr); /* Emulate the eof flag, which isn't normally needed in NFSv2 @@ -625,20 +623,11 @@ static void nfs_read_done(struct rpc_task *task, void *calldata) if (data->args.offset + data->args.count >= data->res.fattr->size) data->res.eof = 1; } - nfs_readpage_result(task, calldata); + return 0; } -static const struct rpc_call_ops nfs_read_ops = { - .rpc_call_done = nfs_read_done, - .rpc_release = nfs_readdata_release, -}; - -static void -nfs_proc_read_setup(struct nfs_read_data *data) +static void nfs_proc_read_setup(struct nfs_read_data *data) { - struct rpc_task *task = &data->task; - struct inode *inode = data->inode; - int flags; struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_READ], .rpc_argp = &data->args, @@ -646,12 +635,7 @@ nfs_proc_read_setup(struct nfs_read_data *data) .rpc_cred = data->cred, }; - /* N.B. Do we need to test? Never called for swapfile inode */ - flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); - - /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs_read_ops, data); - rpc_call_setup(task, &msg, 0); + rpc_call_setup(&data->task, &msg, 0); } static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data) @@ -720,6 +704,7 @@ struct nfs_rpc_ops nfs_v2_clientops = { .pathconf = nfs_proc_pathconf, .decode_dirent = nfs_decode_dirent, .read_setup = nfs_proc_read_setup, + .read_done = nfs_read_done, .write_setup = nfs_proc_write_setup, .write_done = nfs_write_done, .commit_setup = nfs_proc_commit_setup, diff --git a/fs/nfs/read.c b/fs/nfs/read.c index ae3ddd24cf8f..2da255f0247f 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -36,8 +36,8 @@ #define NFSDBG_FACILITY NFSDBG_PAGECACHE static int nfs_pagein_one(struct list_head *, struct inode *); -static void nfs_readpage_result_partial(struct nfs_read_data *, int); -static void nfs_readpage_result_full(struct nfs_read_data *, int); +static const struct rpc_call_ops nfs_read_partial_ops; +static const struct rpc_call_ops nfs_read_full_ops; static kmem_cache_t *nfs_rdata_cachep; mempool_t *nfs_rdata_mempool; @@ -200,9 +200,11 @@ static void nfs_readpage_release(struct nfs_page *req) * Set up the NFS read request struct */ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, + const struct rpc_call_ops *call_ops, unsigned int count, unsigned int offset) { struct inode *inode; + int flags; data->req = req; data->inode = inode = req->wb_context->dentry->d_inode; @@ -220,6 +222,9 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, data->res.eof = 0; nfs_fattr_init(&data->fattr); + /* Set up the initial task struct. */ + flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); + rpc_init_task(&data->task, NFS_CLIENT(inode), flags, call_ops, data); NFS_PROTO(inode)->read_setup(data); data->task.tk_cookie = (unsigned long)inode; @@ -307,14 +312,15 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode) list_del_init(&data->pages); data->pagevec[0] = page; - data->complete = nfs_readpage_result_partial; if (nbytes > rsize) { - nfs_read_rpcsetup(req, data, rsize, offset); + nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, + rsize, offset); offset += rsize; nbytes -= rsize; } else { - nfs_read_rpcsetup(req, data, nbytes, offset); + nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, + nbytes, offset); nbytes = 0; } nfs_execute_read(data); @@ -360,8 +366,7 @@ static int nfs_pagein_one(struct list_head *head, struct inode *inode) } req = nfs_list_entry(data->pages.next); - data->complete = nfs_readpage_result_full; - nfs_read_rpcsetup(req, data, count, 0); + nfs_read_rpcsetup(req, data, &nfs_read_full_ops, count, 0); nfs_execute_read(data); return 0; @@ -395,12 +400,15 @@ nfs_pagein_list(struct list_head *head, int rpages) /* * Handle a read reply that fills part of a page. */ -static void nfs_readpage_result_partial(struct nfs_read_data *data, int status) +static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata) { + struct nfs_read_data *data = calldata; struct nfs_page *req = data->req; struct page *page = req->wb_page; - if (status >= 0) { + if (nfs_readpage_result(task, data) != 0) + return; + if (task->tk_status >= 0) { unsigned int request = data->args.count; unsigned int result = data->res.count; @@ -419,20 +427,28 @@ static void nfs_readpage_result_partial(struct nfs_read_data *data, int status) } } +static const struct rpc_call_ops nfs_read_partial_ops = { + .rpc_call_done = nfs_readpage_result_partial, + .rpc_release = nfs_readdata_release, +}; + /* * This is the callback from RPC telling us whether a reply was * received or some error occurred (timeout or socket shutdown). */ -static void nfs_readpage_result_full(struct nfs_read_data *data, int status) +static void nfs_readpage_result_full(struct rpc_task *task, void *calldata) { + struct nfs_read_data *data = calldata; unsigned int count = data->res.count; + if (nfs_readpage_result(task, data) != 0) + return; while (!list_empty(&data->pages)) { struct nfs_page *req = nfs_list_entry(data->pages.next); struct page *page = req->wb_page; nfs_list_remove_request(req); - if (status >= 0) { + if (task->tk_status >= 0) { if (count < PAGE_CACHE_SIZE) { if (count < req->wb_bytes) memclear_highpage_flush(page, @@ -448,19 +464,27 @@ static void nfs_readpage_result_full(struct nfs_read_data *data, int status) } } +static const struct rpc_call_ops nfs_read_full_ops = { + .rpc_call_done = nfs_readpage_result_full, + .rpc_release = nfs_readdata_release, +}; + /* * This is the callback from RPC telling us whether a reply was * received or some error occurred (timeout or socket shutdown). */ -void nfs_readpage_result(struct rpc_task *task, void *calldata) +int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) { - struct nfs_read_data *data = calldata; struct nfs_readargs *argp = &data->args; struct nfs_readres *resp = &data->res; - int status = task->tk_status; + int status; dprintk("NFS: %4d nfs_readpage_result, (status %d)\n", - task->tk_pid, status); + task->tk_pid, task->tk_status); + + status = NFS_PROTO(data->inode)->read_done(task, data); + if (status != 0) + return status; nfs_add_stats(data->inode, NFSIOS_SERVERREADBYTES, resp->count); @@ -474,14 +498,14 @@ void nfs_readpage_result(struct rpc_task *task, void *calldata) argp->pgbase += resp->count; argp->count -= resp->count; rpc_restart_call(task); - return; + return -EAGAIN; } task->tk_status = -EIO; } spin_lock(&data->inode->i_lock); NFS_I(data->inode)->cache_validity |= NFS_INO_INVALID_ATIME; spin_unlock(&data->inode->i_lock); - data->complete(data, status); + return 0; } /* diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 782e59765696..f55827be4f8e 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -492,8 +492,8 @@ static inline void nfs_writedata_free(struct nfs_write_data *p) extern int nfs_readpage(struct file *, struct page *); extern int nfs_readpages(struct file *, struct address_space *, struct list_head *, unsigned); -extern void nfs_readpage_result(struct rpc_task *, void *); -extern void nfs_readdata_release(void *data); +extern int nfs_readpage_result(struct rpc_task *, struct nfs_read_data *); +extern void nfs_readdata_release(void *data); /* diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 277750cc70c0..7fafc4c546b7 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -695,7 +695,6 @@ struct nfs_read_data { #ifdef CONFIG_NFS_V4 unsigned long timestamp; /* For lease renewal */ #endif - void (*complete) (struct nfs_read_data *, int); struct page *page_array[NFS_PAGEVEC_SIZE + 1]; }; @@ -768,6 +767,7 @@ struct nfs_rpc_ops { struct nfs_pathconf *); u32 * (*decode_dirent)(u32 *, struct nfs_entry *, int plus); void (*read_setup) (struct nfs_read_data *); + int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, int how); int (*write_done) (struct rpc_task *, struct nfs_write_data *); void (*commit_setup) (struct nfs_write_data *, int how); -- cgit v1.2.3 From 462d5b3296b56289efec426499a83faad4c08d9e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 20 Mar 2006 13:44:32 -0500 Subject: NFS: make direct write path generate write requests concurrently Duplicate infrastructure from direct read path that will allow write path to generate multiple write requests concurrently. This will enable us to add support for aio in this path. Temporarily we will lose the ability to do UNSTABLE writes followed by a COMMIT in the direct write path. However, all applications I am aware of that use NFS O_DIRECT currently write in relatively small chunks, so this should not be inconvenient in any way. Test plan: Millions of fsx-odirect ops. OraSim. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 240 ++++++++++++++++++++++++++++++++----------------- fs/nfs/write.c | 3 +- include/linux/nfs_fs.h | 2 + 3 files changed, 162 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 4df21ce28e17..dea3239cdded 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -384,106 +384,185 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size return result; } -static ssize_t nfs_direct_write_seg(struct inode *inode, struct nfs_open_context *ctx, unsigned long user_addr, size_t count, loff_t file_offset, struct page **pages, int nr_pages) +static struct nfs_direct_req *nfs_direct_write_alloc(size_t nbytes, size_t wsize) { - const unsigned int wsize = NFS_SERVER(inode)->wsize; - size_t request; - int curpage, need_commit; - ssize_t result, tot_bytes; - struct nfs_writeverf first_verf; - struct nfs_write_data *wdata; - - wdata = nfs_writedata_alloc(NFS_SERVER(inode)->wpages); - if (!wdata) - return -ENOMEM; + struct list_head *list; + struct nfs_direct_req *dreq; + unsigned int writes = 0; + unsigned int wpages = (wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - wdata->inode = inode; - wdata->cred = ctx->cred; - wdata->args.fh = NFS_FH(inode); - wdata->args.context = ctx; - wdata->args.stable = NFS_UNSTABLE; - if (IS_SYNC(inode) || NFS_PROTO(inode)->version == 2 || count <= wsize) - wdata->args.stable = NFS_FILE_SYNC; - wdata->res.fattr = &wdata->fattr; - wdata->res.verf = &wdata->verf; + dreq = nfs_direct_req_alloc(); + if (!dreq) + return NULL; + + list = &dreq->list; + for(;;) { + struct nfs_write_data *data = nfs_writedata_alloc(wpages); + + if (unlikely(!data)) { + while (!list_empty(list)) { + data = list_entry(list->next, + struct nfs_write_data, pages); + list_del(&data->pages); + nfs_writedata_free(data); + } + kref_put(&dreq->kref, nfs_direct_req_release); + return NULL; + } + + INIT_LIST_HEAD(&data->pages); + list_add(&data->pages, list); + + data->req = (struct nfs_page *) dreq; + writes++; + if (nbytes <= wsize) + break; + nbytes -= wsize; + } + kref_get(&dreq->kref); + atomic_set(&dreq->complete, writes); + return dreq; +} + +/* + * Collects and returns the final error value/byte-count. + */ +static ssize_t nfs_direct_write_wait(struct nfs_direct_req *dreq, int intr) +{ + int result = 0; + + if (intr) { + result = wait_event_interruptible(dreq->wait, + (atomic_read(&dreq->complete) == 0)); + } else { + wait_event(dreq->wait, (atomic_read(&dreq->complete) == 0)); + } + + if (!result) + result = atomic_read(&dreq->error); + if (!result) + result = atomic_read(&dreq->count); + + kref_put(&dreq->kref, nfs_direct_req_release); + return (ssize_t) result; +} + +static void nfs_direct_write_result(struct rpc_task *task, void *calldata) +{ + struct nfs_write_data *data = calldata; + struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; + int status = task->tk_status; + + if (nfs_writeback_done(task, data) != 0) + return; + /* If the server fell back to an UNSTABLE write, it's an error. */ + if (unlikely(data->res.verf->committed != NFS_FILE_SYNC)) + status = -EIO; + + if (likely(status >= 0)) + atomic_add(data->res.count, &dreq->count); + else + atomic_set(&dreq->error, status); + + if (unlikely(atomic_dec_and_test(&dreq->complete))) + nfs_direct_complete(dreq); +} + +static const struct rpc_call_ops nfs_write_direct_ops = { + .rpc_call_done = nfs_direct_write_result, + .rpc_release = nfs_writedata_release, +}; + +/* + * For each nfs_write_data struct that was allocated on the list, dispatch + * an NFS WRITE operation + * + * XXX: For now, support only FILE_SYNC writes. Later we may add + * support for UNSTABLE + COMMIT. + */ +static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, struct inode *inode, struct nfs_open_context *ctx, unsigned long user_addr, size_t count, loff_t file_offset) +{ + struct list_head *list = &dreq->list; + struct page **pages = dreq->pages; + size_t wsize = NFS_SERVER(inode)->wsize; + unsigned int curpage, pgbase; - nfs_begin_data_update(inode); -retry: - need_commit = 0; - tot_bytes = 0; curpage = 0; - request = count; - wdata->args.pgbase = user_addr & ~PAGE_MASK; - wdata->args.offset = file_offset; + pgbase = user_addr & ~PAGE_MASK; do { - wdata->args.count = request; - if (wdata->args.count > wsize) - wdata->args.count = wsize; - wdata->args.pages = &pages[curpage]; + struct nfs_write_data *data; + size_t bytes; + + bytes = wsize; + if (count < wsize) + bytes = count; + + data = list_entry(list->next, struct nfs_write_data, pages); + list_del_init(&data->pages); + + data->inode = inode; + data->cred = ctx->cred; + data->args.fh = NFS_FH(inode); + data->args.context = ctx; + data->args.offset = file_offset; + data->args.pgbase = pgbase; + data->args.pages = &pages[curpage]; + data->args.count = bytes; + data->res.fattr = &data->fattr; + data->res.count = bytes; + + rpc_init_task(&data->task, NFS_CLIENT(inode), RPC_TASK_ASYNC, + &nfs_write_direct_ops, data); + NFS_PROTO(inode)->write_setup(data, FLUSH_STABLE); - dprintk("NFS: direct write: c=%u o=%Ld ua=%lu, pb=%u, cp=%u\n", - wdata->args.count, (long long) wdata->args.offset, - user_addr + tot_bytes, wdata->args.pgbase, curpage); + data->task.tk_priority = RPC_PRIORITY_NORMAL; + data->task.tk_cookie = (unsigned long) inode; lock_kernel(); - result = NFS_PROTO(inode)->write(wdata); + rpc_execute(&data->task); unlock_kernel(); - if (result <= 0) { - if (tot_bytes > 0) - break; - goto out; - } + dfprintk(VFS, "NFS: %4d initiated direct write call (req %s/%Ld, %u bytes @ offset %Lu)\n", + data->task.tk_pid, + inode->i_sb->s_id, + (long long)NFS_FILEID(inode), + bytes, + (unsigned long long)data->args.offset); - if (tot_bytes == 0) - memcpy(&first_verf.verifier, &wdata->verf.verifier, - sizeof(first_verf.verifier)); - if (wdata->verf.committed != NFS_FILE_SYNC) { - need_commit = 1; - if (memcmp(&first_verf.verifier, &wdata->verf.verifier, - sizeof(first_verf.verifier))) - goto sync_retry; - } + file_offset += bytes; + pgbase += bytes; + curpage += pgbase >> PAGE_SHIFT; + pgbase &= ~PAGE_MASK; - tot_bytes += result; + count -= bytes; + } while (count != 0); +} - /* in case of a short write: stop now, let the app recover */ - if (result < wdata->args.count) - break; +static ssize_t nfs_direct_write_seg(struct inode *inode, struct nfs_open_context *ctx, unsigned long user_addr, size_t count, loff_t file_offset, struct page **pages, int nr_pages) +{ + ssize_t result; + sigset_t oldset; + struct rpc_clnt *clnt = NFS_CLIENT(inode); + struct nfs_direct_req *dreq; - wdata->args.offset += result; - wdata->args.pgbase += result; - curpage += wdata->args.pgbase >> PAGE_SHIFT; - wdata->args.pgbase &= ~PAGE_MASK; - request -= result; - } while (request != 0); + dreq = nfs_direct_write_alloc(count, NFS_SERVER(inode)->wsize); + if (!dreq) + return -ENOMEM; - /* - * Commit data written so far, even in the event of an error - */ - if (need_commit) { - wdata->args.count = tot_bytes; - wdata->args.offset = file_offset; + dreq->pages = pages; + dreq->npages = nr_pages; - lock_kernel(); - result = NFS_PROTO(inode)->commit(wdata); - unlock_kernel(); + nfs_begin_data_update(inode); - if (result < 0 || memcmp(&first_verf.verifier, - &wdata->verf.verifier, - sizeof(first_verf.verifier)) != 0) - goto sync_retry; - } - result = tot_bytes; + rpc_clnt_sigmask(clnt, &oldset); + nfs_direct_write_schedule(dreq, inode, ctx, user_addr, count, + file_offset); + result = nfs_direct_write_wait(dreq, clnt->cl_intr); + rpc_clnt_sigunmask(clnt, &oldset); -out: nfs_end_data_update(inode); - nfs_writedata_free(wdata); - return result; -sync_retry: - wdata->args.stable = NFS_FILE_SYNC; - goto retry; + return result; } /* @@ -515,7 +594,6 @@ static ssize_t nfs_direct_write(struct inode *inode, struct nfs_open_context *ct nfs_add_stats(inode, NFSIOS_DIRECTWRITTENBYTES, size); result = nfs_direct_write_seg(inode, ctx, user_addr, size, file_offset, pages, page_count); - nfs_free_user_pages(pages, page_count, 0); if (result <= 0) { if (tot_bytes > 0) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 5912274ff1a1..875f5b060533 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -77,7 +77,6 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context*, struct inode *, struct page *, unsigned int, unsigned int); -static int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *); static int nfs_wait_on_write_congestion(struct address_space *, int); static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int); static int nfs_flush_inode(struct inode *inode, unsigned long idx_start, @@ -1183,7 +1182,7 @@ static const struct rpc_call_ops nfs_write_full_ops = { /* * This function is called when the WRITE call is complete. */ -static int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) +int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) { struct nfs_writeargs *argp = &data->args; struct nfs_writeres *resp = &data->res; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index f55827be4f8e..6c130a6b0f4d 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -407,6 +407,8 @@ extern int nfs_writepage(struct page *page, struct writeback_control *wbc); extern int nfs_writepages(struct address_space *, struct writeback_control *); extern int nfs_flush_incompatible(struct file *file, struct page *page); extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int); +extern int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *); +extern void nfs_writedata_release(void *); /* * Try to write back everything synchronously (but check the -- cgit v1.2.3 From e17b1fc4b35399935f00a635206e183d9292fe4f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:35 -0500 Subject: NFS: Make nfs_commit_alloc() extern We need to use nfs_commit_alloc() in fs/nfs/direct.c. Signed-off-by: Trond Myklebust --- fs/nfs/write.c | 4 ++-- include/linux/nfs_fs.h | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 875f5b060533..f7c8be0beccf 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -91,7 +91,7 @@ static mempool_t *nfs_commit_mempool; static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion); -static inline struct nfs_write_data *nfs_commit_alloc(unsigned int pagecount) +struct nfs_write_data *nfs_commit_alloc(unsigned int pagecount) { struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, SLAB_NOFS); @@ -112,7 +112,7 @@ static inline struct nfs_write_data *nfs_commit_alloc(unsigned int pagecount) return p; } -static inline void nfs_commit_free(struct nfs_write_data *p) +void nfs_commit_free(struct nfs_write_data *p) { if (p && (p->pagevec != &p->page_array[0])) kfree(p->pagevec); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 6c130a6b0f4d..423f202b881c 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -410,6 +410,11 @@ extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned extern int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *); extern void nfs_writedata_release(void *); +#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +struct nfs_write_data *nfs_commit_alloc(unsigned int pagecount); +void nfs_commit_free(struct nfs_write_data *p); +#endif + /* * Try to write back everything synchronously (but check the * return value!) -- cgit v1.2.3 From fad61490419b3e494f300e9b2579810ef3bcda31 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:36 -0500 Subject: nfs: Use UNSTABLE + COMMIT for NFS O_DIRECT writes Currently NFS O_DIRECT writes use FILE_SYNC so that a COMMIT is not necessary. This simplifies the internal logic, but this could be a difficult workload for some servers. Instead, let's send UNSTABLE writes, and after they all complete, send a COMMIT for the dirty range. After the COMMIT returns successfully, then do the wake_up or fire off aio_complete(). Test plan: Async direct I/O tests against Solaris (or any server that requires committed unstable writes). Reboot server during test. Based on an earlier patch by Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 224 +++++++++++++++++++++++++++++++++++++++++++------ include/linux/nfs_fs.h | 1 + 2 files changed, 200 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 737990dd4dfe..f0f2053c7a61 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -69,11 +69,15 @@ struct nfs_direct_req { struct kref kref; /* release manager */ /* I/O parameters */ - struct list_head list; /* nfs_read/write_data structs */ + struct list_head list, /* nfs_read/write_data structs */ + rewrite_list; /* saved nfs_write_data structs */ struct file * filp; /* file descriptor */ struct kiocb * iocb; /* controlling i/o request */ wait_queue_head_t wait; /* wait for i/o completion */ struct inode * inode; /* target file of i/o */ + unsigned long user_addr; /* location of user's buffer */ + size_t user_count; /* total bytes to move */ + loff_t pos; /* starting offset in file */ struct page ** pages; /* pages in our buffer */ unsigned int npages; /* count of pages */ @@ -82,8 +86,18 @@ struct nfs_direct_req { int outstanding; /* i/os we're waiting for */ ssize_t count, /* bytes actually processed */ error; /* any reported error */ + + /* commit state */ + struct nfs_write_data * commit_data; /* special write_data for commits */ + int flags; +#define NFS_ODIRECT_DO_COMMIT (1) /* an unstable reply was received */ +#define NFS_ODIRECT_RESCHED_WRITES (2) /* write verification failed */ + struct nfs_writeverf verf; /* unstable write verifier */ }; +static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync); +static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode); + /** * nfs_direct_IO - NFS address space operation for direct I/O * @rw: direction (read or write) @@ -160,11 +174,13 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void) kref_init(&dreq->kref); init_waitqueue_head(&dreq->wait); INIT_LIST_HEAD(&dreq->list); + INIT_LIST_HEAD(&dreq->rewrite_list); dreq->iocb = NULL; spin_lock_init(&dreq->lock); dreq->outstanding = 0; dreq->count = 0; dreq->error = 0; + dreq->flags = 0; return dreq; } @@ -299,7 +315,7 @@ static const struct rpc_call_ops nfs_read_direct_ops = { * For each nfs_read_data struct that was allocated on the list, dispatch * an NFS READ operation */ -static void nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos) +static void nfs_direct_read_schedule(struct nfs_direct_req *dreq) { struct file *file = dreq->filp; struct inode *inode = file->f_mapping->host; @@ -307,11 +323,13 @@ static void nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long file->private_data; struct list_head *list = &dreq->list; struct page **pages = dreq->pages; + size_t count = dreq->user_count; + loff_t pos = dreq->pos; size_t rsize = NFS_SERVER(inode)->rsize; unsigned int curpage, pgbase; curpage = 0; - pgbase = user_addr & ~PAGE_MASK; + pgbase = dreq->user_addr & ~PAGE_MASK; do { struct nfs_read_data *data; size_t bytes; @@ -373,6 +391,9 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size if (!dreq) return -ENOMEM; + dreq->user_addr = user_addr; + dreq->user_count = count; + dreq->pos = pos; dreq->pages = pages; dreq->npages = nr_pages; igrab(inode); @@ -383,13 +404,137 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count); rpc_clnt_sigmask(clnt, &oldset); - nfs_direct_read_schedule(dreq, user_addr, count, pos); + nfs_direct_read_schedule(dreq); result = nfs_direct_wait(dreq); rpc_clnt_sigunmask(clnt, &oldset); return result; } +static void nfs_direct_free_writedata(struct nfs_direct_req *dreq) +{ + list_splice_init(&dreq->rewrite_list, &dreq->list); + while (!list_empty(&dreq->list)) { + struct nfs_write_data *data = list_entry(dreq->list.next, struct nfs_write_data, pages); + list_del(&data->pages); + nfs_writedata_release(data); + } +} + +#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) +{ + struct list_head *pos; + + list_splice_init(&dreq->rewrite_list, &dreq->list); + list_for_each(pos, &dreq->list) + dreq->outstanding++; + dreq->count = 0; + + nfs_direct_write_schedule(dreq, FLUSH_STABLE); +} + +static void nfs_direct_commit_result(struct rpc_task *task, void *calldata) +{ + struct nfs_write_data *data = calldata; + struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; + + /* Call the NFS version-specific code */ + if (NFS_PROTO(data->inode)->commit_done(task, data) != 0) + return; + if (unlikely(task->tk_status < 0)) { + dreq->error = task->tk_status; + dreq->flags = NFS_ODIRECT_RESCHED_WRITES; + } + if (memcmp(&dreq->verf, &data->verf, sizeof(data->verf))) { + dprintk("NFS: %5u commit verify failed\n", task->tk_pid); + dreq->flags = NFS_ODIRECT_RESCHED_WRITES; + } + + dprintk("NFS: %5u commit returned %d\n", task->tk_pid, task->tk_status); + nfs_direct_write_complete(dreq, data->inode); +} + +static const struct rpc_call_ops nfs_commit_direct_ops = { + .rpc_call_done = nfs_direct_commit_result, + .rpc_release = nfs_commit_release, +}; + +static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq) +{ + struct file *file = dreq->filp; + struct nfs_open_context *ctx = (struct nfs_open_context *) + file->private_data; + struct nfs_write_data *data = dreq->commit_data; + struct rpc_task *task = &data->task; + + data->inode = dreq->inode; + data->cred = ctx->cred; + + data->args.fh = NFS_FH(data->inode); + data->args.offset = dreq->pos; + data->args.count = dreq->user_count; + data->res.count = 0; + data->res.fattr = &data->fattr; + data->res.verf = &data->verf; + + rpc_init_task(&data->task, NFS_CLIENT(dreq->inode), RPC_TASK_ASYNC, + &nfs_commit_direct_ops, data); + NFS_PROTO(data->inode)->commit_setup(data, 0); + + data->task.tk_priority = RPC_PRIORITY_NORMAL; + data->task.tk_cookie = (unsigned long)data->inode; + /* 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); + + lock_kernel(); + rpc_execute(&data->task); + unlock_kernel(); +} + +static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode) +{ + int flags = dreq->flags; + + dreq->flags = 0; + switch (flags) { + case NFS_ODIRECT_DO_COMMIT: + nfs_direct_commit_schedule(dreq); + break; + case NFS_ODIRECT_RESCHED_WRITES: + nfs_direct_write_reschedule(dreq); + break; + default: + nfs_end_data_update(inode); + if (dreq->commit_data != NULL) + nfs_commit_free(dreq->commit_data); + nfs_direct_free_writedata(dreq); + nfs_direct_complete(dreq); + } +} + +static void nfs_alloc_commit_data(struct nfs_direct_req *dreq) +{ + dreq->commit_data = nfs_commit_alloc(0); + if (dreq->commit_data != NULL) + dreq->commit_data->req = (struct nfs_page *) dreq; +} +#else +static inline void nfs_alloc_commit_data(struct nfs_direct_req *dreq) +{ + dreq->commit_data = NULL; +} + +static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode) +{ + nfs_end_data_update(inode); + nfs_direct_free_writedata(dreq); + nfs_direct_complete(dreq); +} +#endif + static struct nfs_direct_req *nfs_direct_write_alloc(size_t nbytes, size_t wsize) { struct list_head *list; @@ -424,14 +569,13 @@ static struct nfs_direct_req *nfs_direct_write_alloc(size_t nbytes, size_t wsize break; nbytes -= wsize; } + + nfs_alloc_commit_data(dreq); + kref_get(&dreq->kref); return dreq; } -/* - * NB: Return the value of the first error return code. Subsequent - * errors after the first one are ignored. - */ static void nfs_direct_write_result(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; @@ -440,41 +584,62 @@ static void nfs_direct_write_result(struct rpc_task *task, void *calldata) if (nfs_writeback_done(task, data) != 0) return; - /* If the server fell back to an UNSTABLE write, it's an error. */ - if (unlikely(data->res.verf->committed != NFS_FILE_SYNC)) - status = -EIO; spin_lock(&dreq->lock); if (likely(status >= 0)) dreq->count += data->res.count; else - dreq->error = status; + dreq->error = task->tk_status; + if (data->res.verf->committed != NFS_FILE_SYNC) { + switch (dreq->flags) { + case 0: + memcpy(&dreq->verf, &data->verf, sizeof(dreq->verf)); + dreq->flags = NFS_ODIRECT_DO_COMMIT; + break; + case NFS_ODIRECT_DO_COMMIT: + if (memcmp(&dreq->verf, &data->verf, sizeof(dreq->verf))) { + dprintk("NFS: %5u write verify failed\n", task->tk_pid); + dreq->flags = NFS_ODIRECT_RESCHED_WRITES; + } + } + } + /* In case we have to resend */ + data->args.stable = NFS_FILE_SYNC; + + spin_unlock(&dreq->lock); +} + +/* + * NB: Return the value of the first error return code. Subsequent + * errors after the first one are ignored. + */ +static void nfs_direct_write_release(void *calldata) +{ + struct nfs_write_data *data = calldata; + struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; + + spin_lock(&dreq->lock); if (--dreq->outstanding) { spin_unlock(&dreq->lock); return; } - spin_unlock(&dreq->lock); - nfs_end_data_update(data->inode); - nfs_direct_complete(dreq); + nfs_direct_write_complete(dreq, data->inode); } static const struct rpc_call_ops nfs_write_direct_ops = { .rpc_call_done = nfs_direct_write_result, - .rpc_release = nfs_writedata_release, + .rpc_release = nfs_direct_write_release, }; /* * For each nfs_write_data struct that was allocated on the list, dispatch * an NFS WRITE operation - * - * XXX: For now, support only FILE_SYNC writes. Later we may add - * support for UNSTABLE + COMMIT. */ -static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos) +static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync) { struct file *file = dreq->filp; struct inode *inode = file->f_mapping->host; @@ -482,11 +647,13 @@ static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long file->private_data; struct list_head *list = &dreq->list; struct page **pages = dreq->pages; + size_t count = dreq->user_count; + loff_t pos = dreq->pos; size_t wsize = NFS_SERVER(inode)->wsize; unsigned int curpage, pgbase; curpage = 0; - pgbase = user_addr & ~PAGE_MASK; + pgbase = dreq->user_addr & ~PAGE_MASK; do { struct nfs_write_data *data; size_t bytes; @@ -496,7 +663,7 @@ static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long bytes = count; data = list_entry(list->next, struct nfs_write_data, pages); - list_del_init(&data->pages); + list_move_tail(&data->pages, &dreq->rewrite_list); data->inode = inode; data->cred = ctx->cred; @@ -512,7 +679,7 @@ static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long rpc_init_task(&data->task, NFS_CLIENT(inode), RPC_TASK_ASYNC, &nfs_write_direct_ops, data); - NFS_PROTO(inode)->write_setup(data, FLUSH_STABLE); + NFS_PROTO(inode)->write_setup(data, sync); data->task.tk_priority = RPC_PRIORITY_NORMAL; data->task.tk_cookie = (unsigned long) inode; @@ -544,11 +711,18 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz struct inode *inode = iocb->ki_filp->f_mapping->host; struct rpc_clnt *clnt = NFS_CLIENT(inode); struct nfs_direct_req *dreq; + size_t wsize = NFS_SERVER(inode)->wsize; + int sync = 0; - dreq = nfs_direct_write_alloc(count, NFS_SERVER(inode)->wsize); + dreq = nfs_direct_write_alloc(count, wsize); if (!dreq) return -ENOMEM; + if (dreq->commit_data == NULL || count < wsize) + sync = FLUSH_STABLE; + dreq->user_addr = user_addr; + dreq->user_count = count; + dreq->pos = pos; dreq->pages = pages; dreq->npages = nr_pages; igrab(inode); @@ -562,7 +736,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz nfs_begin_data_update(inode); rpc_clnt_sigmask(clnt, &oldset); - nfs_direct_write_schedule(dreq, user_addr, count, pos); + nfs_direct_write_schedule(dreq, sync); result = nfs_direct_wait(dreq); rpc_clnt_sigunmask(clnt, &oldset); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 423f202b881c..9f84c8a5ea43 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -422,6 +422,7 @@ void nfs_commit_free(struct nfs_write_data *p); extern int nfs_sync_inode(struct inode *, unsigned long, unsigned int, int); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) extern int nfs_commit_inode(struct inode *, int); +extern void nfs_commit_release(void *wdata); #else static inline int nfs_commit_inode(struct inode *inode, int how) -- cgit v1.2.3 From 3feb2d49394b7874348a6e43c076b780c1d222c5 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:37 -0500 Subject: NFS: Uninline nfs_writedata_(alloc|free) and nfs_readdata_(alloc|free) Signed-off-by: Trond Myklebust --- fs/nfs/read.c | 32 +++++++++++++++++++++++- fs/nfs/write.c | 32 +++++++++++++++++++++++- include/linux/nfs_fs.h | 67 +++----------------------------------------------- 3 files changed, 66 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 2da255f0247f..3961524fd4ab 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -40,10 +40,40 @@ static const struct rpc_call_ops nfs_read_partial_ops; static const struct rpc_call_ops nfs_read_full_ops; static kmem_cache_t *nfs_rdata_cachep; -mempool_t *nfs_rdata_mempool; +static mempool_t *nfs_rdata_mempool; #define MIN_POOL_READ (32) +struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) +{ + struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, SLAB_NOFS); + + if (p) { + memset(p, 0, sizeof(*p)); + INIT_LIST_HEAD(&p->pages); + if (pagecount < NFS_PAGEVEC_SIZE) + p->pagevec = &p->page_array[0]; + else { + size_t size = ++pagecount * sizeof(struct page *); + p->pagevec = kmalloc(size, GFP_NOFS); + if (p->pagevec) { + memset(p->pagevec, 0, size); + } else { + mempool_free(p, nfs_rdata_mempool); + p = NULL; + } + } + } + return p; +} + +void nfs_readdata_free(struct nfs_read_data *p) +{ + if (p && (p->pagevec != &p->page_array[0])) + kfree(p->pagevec); + mempool_free(p, nfs_rdata_mempool); +} + void nfs_readdata_release(void *data) { nfs_readdata_free(data); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f7c8be0beccf..647e3217c2e1 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -86,7 +86,7 @@ static const struct rpc_call_ops nfs_write_full_ops; static const struct rpc_call_ops nfs_commit_ops; static kmem_cache_t *nfs_wdata_cachep; -mempool_t *nfs_wdata_mempool; +static mempool_t *nfs_wdata_mempool; static mempool_t *nfs_commit_mempool; static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion); @@ -119,6 +119,36 @@ void nfs_commit_free(struct nfs_write_data *p) mempool_free(p, nfs_commit_mempool); } +struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) +{ + struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, SLAB_NOFS); + + if (p) { + memset(p, 0, sizeof(*p)); + INIT_LIST_HEAD(&p->pages); + if (pagecount < NFS_PAGEVEC_SIZE) + p->pagevec = &p->page_array[0]; + else { + size_t size = ++pagecount * sizeof(struct page *); + p->pagevec = kmalloc(size, GFP_NOFS); + if (p->pagevec) { + memset(p->pagevec, 0, size); + } else { + mempool_free(p, nfs_wdata_mempool); + p = NULL; + } + } + } + return p; +} + +void nfs_writedata_free(struct nfs_write_data *p) +{ + if (p && (p->pagevec != &p->page_array[0])) + kfree(p->pagevec); + mempool_free(p, nfs_wdata_mempool); +} + void nfs_writedata_release(void *wdata) { nfs_writedata_free(wdata); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 9f84c8a5ea43..55de0770df4a 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -462,37 +462,8 @@ static inline int nfs_wb_page(struct inode *inode, struct page* page) /* * Allocate and free nfs_write_data structures */ -extern mempool_t *nfs_wdata_mempool; - -static inline struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) -{ - struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, SLAB_NOFS); - - if (p) { - memset(p, 0, sizeof(*p)); - INIT_LIST_HEAD(&p->pages); - if (pagecount < NFS_PAGEVEC_SIZE) - p->pagevec = &p->page_array[0]; - else { - size_t size = ++pagecount * sizeof(struct page *); - p->pagevec = kmalloc(size, GFP_NOFS); - if (p->pagevec) { - memset(p->pagevec, 0, size); - } else { - mempool_free(p, nfs_wdata_mempool); - p = NULL; - } - } - } - return p; -} - -static inline void nfs_writedata_free(struct nfs_write_data *p) -{ - if (p && (p->pagevec != &p->page_array[0])) - kfree(p->pagevec); - mempool_free(p, nfs_wdata_mempool); -} +extern struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount); +extern void nfs_writedata_free(struct nfs_write_data *p); /* * linux/fs/nfs/read.c @@ -503,41 +474,11 @@ extern int nfs_readpages(struct file *, struct address_space *, extern int nfs_readpage_result(struct rpc_task *, struct nfs_read_data *); extern void nfs_readdata_release(void *data); - /* * Allocate and free nfs_read_data structures */ -extern mempool_t *nfs_rdata_mempool; - -static inline struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) -{ - struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, SLAB_NOFS); - - if (p) { - memset(p, 0, sizeof(*p)); - INIT_LIST_HEAD(&p->pages); - if (pagecount < NFS_PAGEVEC_SIZE) - p->pagevec = &p->page_array[0]; - else { - size_t size = ++pagecount * sizeof(struct page *); - p->pagevec = kmalloc(size, GFP_NOFS); - if (p->pagevec) { - memset(p->pagevec, 0, size); - } else { - mempool_free(p, nfs_rdata_mempool); - p = NULL; - } - } - } - return p; -} - -static inline void nfs_readdata_free(struct nfs_read_data *p) -{ - if (p && (p->pagevec != &p->page_array[0])) - kfree(p->pagevec); - mempool_free(p, nfs_rdata_mempool); -} +extern struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount); +extern void nfs_readdata_free(struct nfs_read_data *p); /* * linux/fs/nfs3proc.c -- cgit v1.2.3 From 6849c0cab69f5d1a0fc7b05fa5bfb3dec53f86df Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:39 -0500 Subject: lockd: Add refcounting to struct nlm_block Otherwise, the block may disappear from underneath us when in nlmsvc_retry_blocked. Signed-off-by: Trond Myklebust --- fs/lockd/svclock.c | 94 ++++++++++++++++++++++----------------------- include/linux/lockd/lockd.h | 3 +- 2 files changed, 47 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 1d3a74df93f3..20caeceffb14 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -39,6 +39,7 @@ #define nlm_deadlock nlm_lck_denied #endif +static void nlmsvc_release_block(struct nlm_block *block); static void nlmsvc_insert_block(struct nlm_block *block, unsigned long); static int nlmsvc_remove_block(struct nlm_block *block); @@ -58,6 +59,7 @@ nlmsvc_insert_block(struct nlm_block *block, unsigned long when) struct nlm_block **bp, *b; dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when); + kref_get(&block->b_count); if (block->b_queued) nlmsvc_remove_block(block); bp = &nlm_blocked; @@ -90,6 +92,7 @@ nlmsvc_remove_block(struct nlm_block *block) if (b == block) { *bp = block->b_next; block->b_queued = 0; + nlmsvc_release_block(block); return 1; } } @@ -123,6 +126,7 @@ nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove) *head = block->b_next; block->b_queued = 0; } + kref_get(&block->b_count); return block; } } @@ -155,6 +159,8 @@ nlmsvc_find_block(struct nlm_cookie *cookie, struct sockaddr_in *sin) break; } + if (block != NULL) + kref_get(&block->b_count); return block; } @@ -188,6 +194,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, memset(block, 0, sizeof(*block)); locks_init_lock(&block->b_call.a_args.lock.fl); locks_init_lock(&block->b_call.a_res.lock.fl); + kref_init(&block->b_count); if (!nlmclnt_setgrantargs(&block->b_call, lock)) goto failed_free; @@ -228,27 +235,24 @@ failed: * It is the caller's responsibility to check whether the file * can be closed hereafter. */ -static int -nlmsvc_delete_block(struct nlm_block *block) +static int nlmsvc_unlink_block(struct nlm_block *block) { - struct file_lock *fl = &block->b_call.a_args.lock.fl; - struct nlm_file *file = block->b_file; - struct nlm_block **bp; int status; - - dprintk("lockd: deleting block %p...\n", block); + dprintk("lockd: unlinking block %p...\n", block); /* Remove block from list */ + status = posix_unblock_lock(block->b_file->f_file, &block->b_call.a_args.lock.fl); nlmsvc_remove_block(block); - status = posix_unblock_lock(file->f_file, fl); + return status; +} - /* If the block is in the middle of a GRANT callback, - * don't kill it yet. */ - if (block->b_incall) { - nlmsvc_insert_block(block, NLM_NEVER); - block->b_done = 1; - return status; - } +static void nlmsvc_free_block(struct kref *kref) +{ + struct nlm_block *block = container_of(kref, struct nlm_block, b_count); + struct nlm_file *file = block->b_file; + struct nlm_block **bp; + + dprintk("lockd: freeing block %p...\n", block); /* Remove block from file's list of blocks */ for (bp = &file->f_blocks; *bp; bp = &(*bp)->b_fnext) { @@ -262,7 +266,12 @@ nlmsvc_delete_block(struct nlm_block *block) nlm_release_host(block->b_host); nlmclnt_freegrantargs(&block->b_call); kfree(block); - return status; +} + +static void nlmsvc_release_block(struct nlm_block *block) +{ + if (block != NULL) + kref_put(&block->b_count, nlmsvc_free_block); } /* @@ -282,7 +291,7 @@ nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action) block->b_host->h_inuse = 1; else if (action == NLM_ACT_UNLOCK) { if (host == NULL || host == block->b_host) - nlmsvc_delete_block(block); + nlmsvc_unlink_block(block); } } up(&file->f_sema); @@ -318,9 +327,9 @@ again: block = nlmsvc_lookup_block(file, lock, 0); if (block == NULL) { if (newblock != NULL) - lock = &newblock->b_call.a_args.lock.fl; + lock = &newblock->b_call.a_args.lock; } else - lock = &block->b_call.a_args.lock.fl; + lock = &block->b_call.a_args.lock; error = posix_lock_file(file->f_file, &lock->fl); lock->fl.fl_flags &= ~FL_SLEEP; @@ -363,12 +372,10 @@ again: /* Append to list of blocked */ nlmsvc_insert_block(newblock, NLM_NEVER); - newblock = NULL; - out: up(&file->f_sema); - if (newblock != NULL) - nlmsvc_delete_block(newblock); + nlmsvc_release_block(newblock); + nlmsvc_release_block(block); dprintk("lockd: nlmsvc_lock returned %u\n", ret); return ret; } @@ -450,8 +457,10 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) (long long)lock->fl.fl_end); down(&file->f_sema); - if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL) - status = nlmsvc_delete_block(block); + if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL) { + status = nlmsvc_unlink_block(block); + nlmsvc_release_block(block); + } up(&file->f_sema); return status ? nlm_lck_denied : nlm_granted; } @@ -514,7 +523,7 @@ nlmsvc_grant_blocked(struct nlm_block *block) down(&file->f_sema); /* Unlink block request from list */ - nlmsvc_remove_block(block); + nlmsvc_unlink_block(block); /* If b_granted is true this means we've been here before. * Just retry the grant callback, possibly refreshing the RPC @@ -525,7 +534,6 @@ nlmsvc_grant_blocked(struct nlm_block *block) } /* Try the lock operation again */ - posix_unblock_lock(file->f_file, &lock->fl); lock->fl.fl_flags |= FL_SLEEP; error = posix_lock_file(file->f_file, &lock->fl); lock->fl.fl_flags &= ~FL_SLEEP; @@ -548,16 +556,15 @@ callback: /* Lock was granted by VFS. */ dprintk("lockd: GRANTing blocked lock.\n"); block->b_granted = 1; - block->b_incall = 1; /* Schedule next grant callback in 30 seconds */ nlmsvc_insert_block(block, 30 * HZ); /* Call the client */ - nlm_get_host(block->b_call.a_host); + kref_get(&block->b_count); if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG, &nlmsvc_grant_ops) < 0) - nlm_release_host(block->b_call.a_host); + nlmsvc_release_block(block); out_unlock: up(&file->f_sema); } @@ -573,20 +580,10 @@ out_unlock: static void nlmsvc_grant_callback(struct rpc_task *task, void *data) { struct nlm_rqst *call = data; - struct nlm_block *block; + struct nlm_block *block = container_of(call, struct nlm_block, b_call); unsigned long timeout; - struct sockaddr_in *peer_addr = RPC_PEERADDR(task->tk_client); dprintk("lockd: GRANT_MSG RPC callback\n"); - dprintk("callback: looking for cookie %s, host (%u.%u.%u.%u)\n", - nlmdbg_cookie2a(&call->a_args.cookie), - NIPQUAD(peer_addr->sin_addr.s_addr)); - if (!(block = nlmsvc_find_block(&call->a_args.cookie, peer_addr))) { - dprintk("lockd: no block for cookie %s, host (%u.%u.%u.%u)\n", - nlmdbg_cookie2a(&call->a_args.cookie), - NIPQUAD(peer_addr->sin_addr.s_addr)); - return; - } /* Technically, we should down the file semaphore here. Since we * move the block towards the head of the queue only, no harm @@ -603,9 +600,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data) } nlmsvc_insert_block(block, timeout); svc_wake_up(block->b_daemon); - block->b_incall = 0; - - nlm_release_host(call->a_host); + nlmsvc_release_block(block); } static const struct rpc_call_ops nlmsvc_grant_ops = { @@ -631,20 +626,19 @@ nlmsvc_grant_reply(struct svc_rqst *rqstp, struct nlm_cookie *cookie, u32 status file->f_count++; down(&file->f_sema); - block = nlmsvc_find_block(cookie, &rqstp->rq_addr); if (block) { if (status == NLM_LCK_DENIED_GRACE_PERIOD) { /* Try again in a couple of seconds */ nlmsvc_insert_block(block, 10 * HZ); - up(&file->f_sema); } else { /* Lock is now held by client, or has been rejected. * In both cases, the block should be removed. */ - nlmsvc_delete_block(block); - up(&file->f_sema); + nlmsvc_unlink_block(block); } } + up(&file->f_sema); nlm_release_file(file); + nlmsvc_release_block(block); } /* @@ -667,10 +661,12 @@ nlmsvc_retry_blocked(void) break; dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n", block, block->b_when, block->b_done); + kref_get(&block->b_count); if (block->b_done) - nlmsvc_delete_block(block); + nlmsvc_unlink_block(block); else nlmsvc_grant_blocked(block); + nlmsvc_release_block(block); } if ((block = nlm_blocked) && block->b_when != NLM_NEVER) diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index ef21ed296039..08ab9773f762 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,7 @@ struct nlm_file { */ #define NLM_NEVER (~(unsigned long) 0) struct nlm_block { + struct kref b_count; /* Reference count */ struct nlm_block * b_next; /* linked list (all blocks) */ struct nlm_block * b_fnext; /* linked list (per file) */ struct nlm_rqst b_call; /* RPC args & callback info */ @@ -119,7 +121,6 @@ struct nlm_block { unsigned int b_id; /* block id */ unsigned char b_queued; /* re-queued */ unsigned char b_granted; /* VFS granted lock */ - unsigned char b_incall; /* doing callback */ unsigned char b_done; /* callback complete */ struct nlm_file * b_file; /* file in question */ }; -- cgit v1.2.3 From 5e1abf8cb713a0b94f5a400c7b9b797990cd9dec Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:39 -0500 Subject: lockd: Clean up of the server-side GRANTED code Signed-off-by: Trond Myklebust --- fs/lockd/clntproc.c | 43 ---------------------------------- fs/lockd/svclock.c | 56 ++++++++++++++++++++++++++++++++++++++++++--- include/linux/lockd/lockd.h | 2 -- 3 files changed, 53 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index acc3eb13a02b..80ae3127699f 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -147,49 +147,6 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req) fl->fl_ops->fl_release_private(fl); } -/* - * Initialize arguments for GRANTED call. The nlm_rqst structure - * has been cleared already. - */ -int -nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) -{ - locks_copy_lock(&call->a_args.lock.fl, &lock->fl); - memcpy(&call->a_args.lock.fh, &lock->fh, sizeof(call->a_args.lock.fh)); - call->a_args.lock.caller = system_utsname.nodename; - call->a_args.lock.oh.len = lock->oh.len; - - /* set default data area */ - call->a_args.lock.oh.data = call->a_owner; - call->a_args.lock.svid = lock->fl.fl_pid; - - if (lock->oh.len > NLMCLNT_OHSIZE) { - void *data = kmalloc(lock->oh.len, GFP_KERNEL); - if (!data) { - nlmclnt_freegrantargs(call); - return 0; - } - call->a_args.lock.oh.data = (u8 *) data; - } - - memcpy(call->a_args.lock.oh.data, lock->oh.data, lock->oh.len); - return 1; -} - -void -nlmclnt_freegrantargs(struct nlm_rqst *call) -{ - struct file_lock *fl = &call->a_args.lock.fl; - /* - * Check whether we allocated memory for the owner. - */ - if (call->a_args.lock.oh.data != (u8 *) call->a_owner) { - kfree(call->a_args.lock.oh.data); - } - if (fl->fl_ops && fl->fl_ops->fl_release_private) - fl->fl_ops->fl_release_private(fl); -} - /* * This is the main entry point for the NLM client. */ diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 20caeceffb14..3c7dd956d9c1 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -43,6 +43,8 @@ static void nlmsvc_release_block(struct nlm_block *block); static void nlmsvc_insert_block(struct nlm_block *block, unsigned long); static int nlmsvc_remove_block(struct nlm_block *block); +static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock); +static void nlmsvc_freegrantargs(struct nlm_rqst *call); static const struct rpc_call_ops nlmsvc_grant_ops; /* @@ -196,7 +198,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, locks_init_lock(&block->b_call.a_res.lock.fl); kref_init(&block->b_count); - if (!nlmclnt_setgrantargs(&block->b_call, lock)) + if (!nlmsvc_setgrantargs(&block->b_call, lock)) goto failed_free; /* Set notifier function for VFS, and init args */ @@ -264,7 +266,7 @@ static void nlmsvc_free_block(struct kref *kref) if (block->b_host) nlm_release_host(block->b_host); - nlmclnt_freegrantargs(&block->b_call); + nlmsvc_freegrantargs(&block->b_call); kfree(block); } @@ -298,6 +300,49 @@ nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action) return 0; } +/* + * Initialize arguments for GRANTED call. The nlm_rqst structure + * has been cleared already. + */ +static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) +{ + locks_copy_lock(&call->a_args.lock.fl, &lock->fl); + memcpy(&call->a_args.lock.fh, &lock->fh, sizeof(call->a_args.lock.fh)); + call->a_args.lock.caller = system_utsname.nodename; + call->a_args.lock.oh.len = lock->oh.len; + + /* set default data area */ + call->a_args.lock.oh.data = call->a_owner; + call->a_args.lock.svid = lock->fl.fl_pid; + + if (lock->oh.len > NLMCLNT_OHSIZE) { + void *data = kmalloc(lock->oh.len, GFP_KERNEL); + if (!data) { + nlmsvc_freegrantargs(call); + return 0; + } + call->a_args.lock.oh.data = (u8 *) data; + } + + memcpy(call->a_args.lock.oh.data, lock->oh.data, lock->oh.len); + return 1; +} + +static void nlmsvc_freegrantargs(struct nlm_rqst *call) +{ + struct file_lock *fl = &call->a_args.lock.fl; + /* + * Check whether we allocated memory for the owner. + */ + if (call->a_args.lock.oh.data != (u8 *) call->a_owner) { + kfree(call->a_args.lock.oh.data); + } + if (fl->fl_ops && fl->fl_ops->fl_release_private) + fl->fl_ops->fl_release_private(fl); + if (fl->fl_lmops && fl->fl_lmops->fl_release_private) + fl->fl_lmops->fl_release_private(fl); +} + /* * Attempt to establish a lock, and if it can't be granted, block it * if required. @@ -600,11 +645,16 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data) } nlmsvc_insert_block(block, timeout); svc_wake_up(block->b_daemon); - nlmsvc_release_block(block); +} + +void nlmsvc_grant_release(void *data) +{ + nlmsvc_release_block(data); } static const struct rpc_call_ops nlmsvc_grant_ops = { .rpc_call_done = nlmsvc_grant_callback, + .rpc_release = nlmsvc_grant_release, }; /* diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 08ab9773f762..860a93f6ce6d 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -153,8 +153,6 @@ long nlmclnt_block(struct nlm_rqst *req, long timeout); u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *); void nlmclnt_recovery(struct nlm_host *, u32); int nlmclnt_reclaim(struct nlm_host *, struct file_lock *); -int nlmclnt_setgrantargs(struct nlm_rqst *, struct nlm_lock *); -void nlmclnt_freegrantargs(struct nlm_rqst *); /* * Host cache -- cgit v1.2.3 From 26bcbf965f857c710adafd16cf424f043006b5dd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 20 Mar 2006 13:44:40 -0500 Subject: lockd: stop abusing file_lock_list Currently lockd directly access the file_lock_list from fs/locks.c. It does so to mark locks granted or reclaimable. This is very suboptimal, because a) lockd needs to poke into locks.c internals, and b) it needs to iterate over all locks in the system for marking locks granted or reclaimable. This patch adds lists for granted and reclaimable locks to the nlm_host structure instead, and adds locks to those. nlmclnt_lock: now adds the lock to h_granted instead of setting the NFS_LCK_GRANTED, still O(1) nlmclnt_mark_reclaim: goes away completely, replaced by a list_splice_init. Complexity reduced from O(locks in the system) to O(1) reclaimer: iterates over h_reclaim now, complexity reduced from O(locks in the system) to O(locks per nlm_host) Signed-off-by: Christoph Hellwig Signed-off-by: Trond Myklebust --- fs/lockd/clntlock.c | 54 ++++++++------------------------------------- fs/lockd/clntproc.c | 11 ++++----- fs/lockd/host.c | 2 ++ fs/locks.c | 5 +---- include/linux/fs.h | 2 -- include/linux/lockd/lockd.h | 2 ++ include/linux/nfs_fs_i.h | 8 +------ 7 files changed, 21 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 8ae79ae4b998..0fc0ee267b04 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -154,34 +154,6 @@ u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock) * server crash. */ -/* - * Mark the locks for reclaiming. - * FIXME: In 2.5 we don't want to iterate through any global file_lock_list. - * Maintain NLM lock reclaiming lists in the nlm_host instead. - */ -static -void nlmclnt_mark_reclaim(struct nlm_host *host) -{ - struct file_lock *fl; - struct inode *inode; - struct list_head *tmp; - - list_for_each(tmp, &file_lock_list) { - fl = list_entry(tmp, struct file_lock, fl_link); - - inode = fl->fl_file->f_dentry->d_inode; - if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) - continue; - if (fl->fl_u.nfs_fl.owner == NULL) - continue; - if (fl->fl_u.nfs_fl.owner->host != host) - continue; - if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) - continue; - fl->fl_u.nfs_fl.flags |= NFS_LCK_RECLAIM; - } -} - /* * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number, * that we mark locks for reclaiming, and that we bump the pseudo NSM state. @@ -194,7 +166,12 @@ void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate) host->h_state++; host->h_nextrebind = 0; nlm_rebind_host(host); - nlmclnt_mark_reclaim(host); + + /* + * Mark the locks for reclaiming. + */ + list_splice_init(&host->h_granted, &host->h_reclaim); + dprintk("NLM: reclaiming locks for host %s", host->h_name); } @@ -223,9 +200,7 @@ reclaimer(void *ptr) { struct nlm_host *host = (struct nlm_host *) ptr; struct nlm_wait *block; - struct list_head *tmp; - struct file_lock *fl; - struct inode *inode; + struct file_lock *fl, *next; daemonize("%s-reclaim", host->h_name); allow_signal(SIGKILL); @@ -237,20 +212,9 @@ reclaimer(void *ptr) /* First, reclaim all locks that have been marked. */ restart: - list_for_each(tmp, &file_lock_list) { - fl = list_entry(tmp, struct file_lock, fl_link); - - inode = fl->fl_file->f_dentry->d_inode; - if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) - continue; - if (fl->fl_u.nfs_fl.owner == NULL) - continue; - if (fl->fl_u.nfs_fl.owner->host != host) - continue; - if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM)) - continue; + list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) { + list_del(&fl->fl_u.nfs_fl.list); - fl->fl_u.nfs_fl.flags &= ~NFS_LCK_RECLAIM; nlmclnt_reclaim(host, fl); if (signalled()) break; diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 80ae3127699f..cb469431bd1d 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -465,7 +465,6 @@ static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *ho { BUG_ON(fl->fl_ops != NULL); fl->fl_u.nfs_fl.state = 0; - fl->fl_u.nfs_fl.flags = 0; fl->fl_u.nfs_fl.owner = nlm_find_lockowner(host, fl->fl_owner); fl->fl_ops = &nlmclnt_lock_ops; } @@ -552,8 +551,8 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) if (resp->status == NLM_LCK_GRANTED) { fl->fl_u.nfs_fl.state = host->h_state; - fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED; fl->fl_flags |= FL_SLEEP; + list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); do_vfs_lock(fl); } status = nlm_stat_to_errno(resp->status); @@ -619,9 +618,11 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) struct nlm_res *resp = &req->a_res; int status; - /* Clean the GRANTED flag now so the lock doesn't get - * reclaimed while we're stuck in the unlock call. */ - fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED; + /* + * Remove from the granted list now so the lock doesn't get + * reclaimed while we're stuck in the unlock call. + */ + list_del(&fl->fl_u.nfs_fl.list); /* * Note: the server is supposed to either grant us the unlock diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 100e78229700..f456f8ed9acd 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -123,6 +123,8 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, nlm_hosts[hash] = host; INIT_LIST_HEAD(&host->h_lockowners); spin_lock_init(&host->h_lock); + INIT_LIST_HEAD(&host->h_granted); + INIT_LIST_HEAD(&host->h_reclaim); if (++nrhosts > NLM_HOST_MAX) next_gc = 0; diff --git a/fs/locks.c b/fs/locks.c index c83b5dbe0ed9..56f996e98bbc 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -139,10 +139,7 @@ int lease_break_time = 45; #define for_each_lock(inode, lockp) \ for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next) -LIST_HEAD(file_lock_list); - -EXPORT_SYMBOL(file_lock_list); - +static LIST_HEAD(file_lock_list); static LIST_HEAD(blocked_list); static kmem_cache_t *filelock_cache; diff --git a/include/linux/fs.h b/include/linux/fs.h index d2cffee8fc11..5dc0fa288a4c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -730,8 +730,6 @@ struct file_lock { #define OFFT_OFFSET_MAX INT_LIMIT(off_t) #endif -extern struct list_head file_lock_list; - #include extern int fcntl_getlk(struct file *, struct flock __user *); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 860a93f6ce6d..b0f63b6ab0d4 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -59,6 +59,8 @@ struct nlm_host { unsigned long h_expires; /* eligible for GC */ struct list_head h_lockowners; /* Lockowners for the client */ spinlock_t h_lock; + struct list_head h_granted; /* Locks in GRANTED state */ + struct list_head h_reclaim; /* Locks in RECLAIM state */ }; /* diff --git a/include/linux/nfs_fs_i.h b/include/linux/nfs_fs_i.h index e2c18dabff86..861730275ba0 100644 --- a/include/linux/nfs_fs_i.h +++ b/include/linux/nfs_fs_i.h @@ -12,8 +12,8 @@ struct nlm_lockowner; */ struct nfs_lock_info { u32 state; - u32 flags; struct nlm_lockowner *owner; + struct list_head list; }; struct nfs4_lock_state; @@ -21,10 +21,4 @@ struct nfs4_lock_info { struct nfs4_lock_state *owner; }; -/* - * Lock flag values - */ -#define NFS_LCK_GRANTED 0x0001 /* lock has been granted */ -#define NFS_LCK_RECLAIM 0x0002 /* lock marked for reclaiming */ - #endif -- cgit v1.2.3 From 3a649b884637c4fdff50a6beebc3dc0e6082e048 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:44 -0500 Subject: NLM: Simplify client locks Signed-off-by: Trond Myklebust --- fs/lockd/clntlock.c | 43 ++++++++++++++++--------------------------- fs/lockd/clntproc.c | 39 ++++++++++++++++----------------------- include/linux/lockd/lockd.h | 7 +++---- 3 files changed, 35 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 7cf41c1e1a88..bce744468708 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -44,32 +44,25 @@ static LIST_HEAD(nlm_blocked); /* * Queue up a lock for blocking so that the GRANTED request can see it */ -int nlmclnt_prepare_block(struct nlm_rqst *req, struct nlm_host *host, struct file_lock *fl) +struct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl) { struct nlm_wait *block; - BUG_ON(req->a_block != NULL); block = kmalloc(sizeof(*block), GFP_KERNEL); - if (block == NULL) - return -ENOMEM; - block->b_host = host; - block->b_lock = fl; - init_waitqueue_head(&block->b_wait); - block->b_status = NLM_LCK_BLOCKED; - - list_add(&block->b_list, &nlm_blocked); - req->a_block = block; - - return 0; + if (block != NULL) { + block->b_host = host; + block->b_lock = fl; + init_waitqueue_head(&block->b_wait); + block->b_status = NLM_LCK_BLOCKED; + list_add(&block->b_list, &nlm_blocked); + } + return block; } -void nlmclnt_finish_block(struct nlm_rqst *req) +void nlmclnt_finish_block(struct nlm_wait *block) { - struct nlm_wait *block = req->a_block; - if (block == NULL) return; - req->a_block = NULL; list_del(&block->b_list); kfree(block); } @@ -77,15 +70,14 @@ void nlmclnt_finish_block(struct nlm_rqst *req) /* * Block on a lock */ -long nlmclnt_block(struct nlm_rqst *req, long timeout) +int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout) { - struct nlm_wait *block = req->a_block; long ret; /* A borken server might ask us to block even if we didn't * request it. Just say no! */ - if (!req->a_args.block) + if (block == NULL) return -EAGAIN; /* Go to sleep waiting for GRANT callback. Some servers seem @@ -99,13 +91,10 @@ long nlmclnt_block(struct nlm_rqst *req, long timeout) ret = wait_event_interruptible_timeout(block->b_wait, block->b_status != NLM_LCK_BLOCKED, timeout); - - if (block->b_status != NLM_LCK_BLOCKED) { - req->a_res.status = block->b_status; - block->b_status = NLM_LCK_BLOCKED; - } - - return ret; + if (ret < 0) + return -ERESTARTSYS; + req->a_res.status = block->b_status; + return 0; } /* diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index c25044f3b660..8af017105854 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -136,15 +136,14 @@ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) (unsigned int)fl->fl_u.nfs_fl.owner->pid, system_utsname.nodename); lock->svid = fl->fl_u.nfs_fl.owner->pid; - locks_copy_lock(&lock->fl, fl); + lock->fl.fl_start = fl->fl_start; + lock->fl.fl_end = fl->fl_end; + lock->fl.fl_type = fl->fl_type; } static void nlmclnt_release_lockargs(struct nlm_rqst *req) { - struct file_lock *fl = &req->a_args.lock.fl; - - if (fl->fl_ops && fl->fl_ops->fl_release_private) - fl->fl_ops->fl_release_private(fl); + BUG_ON(req->a_args.lock.fl.fl_ops != NULL); } /* @@ -455,7 +454,6 @@ static void nlmclnt_locks_release_private(struct file_lock *fl) { list_del(&fl->fl_u.nfs_fl.list); nlm_put_lockowner(fl->fl_u.nfs_fl.owner); - fl->fl_ops = NULL; } static struct file_lock_operations nlmclnt_lock_ops = { @@ -515,41 +513,36 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) { struct nlm_host *host = req->a_host; struct nlm_res *resp = &req->a_res; - long timeout; - int status; + struct nlm_wait *block = NULL; + int status = -ENOLCK; if (!host->h_monitored && nsm_monitor(host) < 0) { printk(KERN_NOTICE "lockd: failed to monitor %s\n", host->h_name); - status = -ENOLCK; goto out; } - if (req->a_args.block) { - status = nlmclnt_prepare_block(req, host, fl); - if (status < 0) - goto out; - } + block = nlmclnt_prepare_block(host, fl); for(;;) { status = nlmclnt_call(req, NLMPROC_LOCK); if (status < 0) goto out_unblock; - if (resp->status != NLM_LCK_BLOCKED) + if (!req->a_args.block) break; - /* Wait on an NLM blocking lock */ - timeout = nlmclnt_block(req, NLMCLNT_POLL_TIMEOUT); /* Did a reclaimer thread notify us of a server reboot? */ if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) continue; if (resp->status != NLM_LCK_BLOCKED) break; - if (timeout >= 0) - continue; - /* We were interrupted. Send a CANCEL request to the server + /* Wait on an NLM blocking lock */ + status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT); + /* if we were interrupted. Send a CANCEL request to the server * and exit */ - status = (int)timeout; - goto out_unblock; + if (status < 0) + goto out_unblock; + if (resp->status != NLM_LCK_BLOCKED) + break; } if (resp->status == NLM_LCK_GRANTED) { @@ -560,7 +553,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) } status = nlm_stat_to_errno(resp->status); out_unblock: - nlmclnt_finish_block(req); + nlmclnt_finish_block(block); /* Cancel the blocked request if it is still pending */ if (resp->status == NLM_LCK_BLOCKED) nlmclnt_cancel(host, req->a_args.block, fl); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index b0f63b6ab0d4..cb9933d04091 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -86,7 +86,6 @@ struct nlm_rqst { struct nlm_host * a_host; /* host handle */ struct nlm_args a_args; /* arguments */ struct nlm_res a_res; /* result */ - struct nlm_wait * a_block; unsigned int a_retries; /* Retry count */ char a_owner[NLMCLNT_OHSIZE]; }; @@ -149,9 +148,9 @@ extern unsigned long nlmsvc_timeout; * Lockd client functions */ struct nlm_rqst * nlmclnt_alloc_call(void); -int nlmclnt_prepare_block(struct nlm_rqst *req, struct nlm_host *host, struct file_lock *fl); -void nlmclnt_finish_block(struct nlm_rqst *req); -long nlmclnt_block(struct nlm_rqst *req, long timeout); +struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl); +void nlmclnt_finish_block(struct nlm_wait *block); +int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout); u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *); void nlmclnt_recovery(struct nlm_host *, u32); int nlmclnt_reclaim(struct nlm_host *, struct file_lock *); -- cgit v1.2.3 From 92737230dd3f1478033819d4bc20339f8da852da Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:45 -0500 Subject: NLM: Add nlmclnt_release_call Add a helper function to simplify the freeing of NLM client requests. Signed-off-by: Trond Myklebust --- fs/lockd/clntproc.c | 195 +++++++++++++++++--------------------------- fs/lockd/svc4proc.c | 34 ++++---- fs/lockd/svclock.c | 66 +++++++-------- fs/lockd/svcproc.c | 33 +++----- include/linux/lockd/lockd.h | 10 ++- 5 files changed, 135 insertions(+), 203 deletions(-) (limited to 'include') diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 7a239864b8bf..3f8ad7c54efa 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -152,9 +152,8 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req) int nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) { - struct nfs_server *nfssrv = NFS_SERVER(inode); struct nlm_host *host; - struct nlm_rqst reqst, *call = &reqst; + struct nlm_rqst *call; sigset_t oldset; unsigned long flags; int status, proto, vers; @@ -168,23 +167,17 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) /* Retrieve transport protocol from NFS client */ proto = NFS_CLIENT(inode)->cl_xprt->prot; - if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers))) + host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers); + if (host == NULL) return -ENOLCK; - /* Create RPC client handle if not there, and copy soft - * and intr flags from NFS client. */ - if (host->h_rpcclnt == NULL) { - struct rpc_clnt *clnt; + call = nlm_alloc_call(host); + if (call == NULL) + return -ENOMEM; - /* Bind an rpc client to this host handle (does not - * perform a portmapper lookup) */ - if (!(clnt = nlm_bind_host(host))) { - status = -ENOLCK; - goto done; - } - clnt->cl_softrtry = nfssrv->client->cl_softrtry; - clnt->cl_intr = nfssrv->client->cl_intr; - } + nlmclnt_locks_init_private(fl, host); + /* Set up the argument struct */ + nlmclnt_setlockargs(call, fl); /* Keep the old signal mask */ spin_lock_irqsave(¤t->sighand->siglock, flags); @@ -197,26 +190,10 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) && (current->flags & PF_EXITING)) { sigfillset(¤t->blocked); /* Mask all signals */ recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - call = nlmclnt_alloc_call(); - if (!call) { - status = -ENOMEM; - goto out_restore; - } call->a_flags = RPC_TASK_ASYNC; - } else { - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - memset(call, 0, sizeof(*call)); - locks_init_lock(&call->a_args.lock.fl); - locks_init_lock(&call->a_res.lock.fl); } - call->a_host = host; - - nlmclnt_locks_init_private(fl, host); - - /* Set up the argument struct */ - nlmclnt_setlockargs(call, fl); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { if (fl->fl_type != F_UNLCK) { @@ -229,24 +206,26 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) else status = -EINVAL; - out_restore: + fl->fl_ops->fl_release_private(fl); + fl->fl_ops = NULL; + spin_lock_irqsave(¤t->sighand->siglock, flags); current->blocked = oldset; recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, flags); -done: dprintk("lockd: clnt proc returns %d\n", status); - nlm_release_host(host); return status; } EXPORT_SYMBOL(nlmclnt_proc); /* * Allocate an NLM RPC call struct + * + * Note: the caller must hold a reference to host. In case of failure, + * this reference will be released. */ -struct nlm_rqst * -nlmclnt_alloc_call(void) +struct nlm_rqst *nlm_alloc_call(struct nlm_host *host) { struct nlm_rqst *call; @@ -255,16 +234,30 @@ nlmclnt_alloc_call(void) if (call != NULL) { locks_init_lock(&call->a_args.lock.fl); locks_init_lock(&call->a_res.lock.fl); + call->a_host = host; return call; } if (signalled()) break; - printk("nlmclnt_alloc_call: failed, waiting for memory\n"); + printk("nlm_alloc_call: failed, waiting for memory\n"); schedule_timeout_interruptible(5*HZ); } + nlm_release_host(host); return NULL; } +void nlm_release_call(struct nlm_rqst *call) +{ + nlm_release_host(call->a_host); + nlmclnt_release_lockargs(call); + kfree(call); +} + +static void nlmclnt_rpc_release(void *data) +{ + return nlm_release_call(data); +} + static int nlm_wait_on_grace(wait_queue_head_t *queue) { DEFINE_WAIT(wait); @@ -361,7 +354,7 @@ in_grace_period: /* * Generic NLM call, async version. */ -int nlmsvc_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) +int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) { struct nlm_host *host = req->a_host; struct rpc_clnt *clnt; @@ -369,48 +362,23 @@ int nlmsvc_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops .rpc_argp = &req->a_args, .rpc_resp = &req->a_res, }; - int status; - - dprintk("lockd: call procedure %d on %s (async)\n", - (int)proc, host->h_name); - - /* If we have no RPC client yet, create one. */ - if ((clnt = nlm_bind_host(host)) == NULL) - return -ENOLCK; - msg.rpc_proc = &clnt->cl_procinfo[proc]; - - /* bootstrap and kick off the async RPC call */ - status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, tk_ops, req); - - return status; -} - -static int nlmclnt_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) -{ - struct nlm_host *host = req->a_host; - struct rpc_clnt *clnt; - struct nlm_args *argp = &req->a_args; - struct nlm_res *resp = &req->a_res; - struct rpc_message msg = { - .rpc_argp = argp, - .rpc_resp = resp, - }; - int status; + int status = -ENOLCK; dprintk("lockd: call procedure %d on %s (async)\n", (int)proc, host->h_name); /* If we have no RPC client yet, create one. */ - if ((clnt = nlm_bind_host(host)) == NULL) - return -ENOLCK; + clnt = nlm_bind_host(host); + if (clnt == NULL) + goto out_err; msg.rpc_proc = &clnt->cl_procinfo[proc]; - /* Increment host refcount */ - nlm_get_host(host); /* bootstrap and kick off the async RPC call */ status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, tk_ops, req); - if (status < 0) - nlm_release_host(host); + if (status == 0) + return 0; +out_err: + nlm_release_call(req); return status; } @@ -423,26 +391,28 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) int status; status = nlmclnt_call(req, NLMPROC_TEST); - nlmclnt_release_lockargs(req); if (status < 0) - return status; + goto out; - status = req->a_res.status; - if (status == NLM_LCK_GRANTED) { - fl->fl_type = F_UNLCK; - } if (status == NLM_LCK_DENIED) { - /* - * Report the conflicting lock back to the application. - */ - fl->fl_start = req->a_res.lock.fl.fl_start; - fl->fl_end = req->a_res.lock.fl.fl_start; - fl->fl_type = req->a_res.lock.fl.fl_type; - fl->fl_pid = 0; - } else { - return nlm_stat_to_errno(req->a_res.status); + switch (req->a_res.status) { + case NLM_LCK_GRANTED: + fl->fl_type = F_UNLCK; + break; + case NLM_LCK_DENIED: + /* + * Report the conflicting lock back to the application. + */ + fl->fl_start = req->a_res.lock.fl.fl_start; + fl->fl_end = req->a_res.lock.fl.fl_start; + fl->fl_type = req->a_res.lock.fl.fl_type; + fl->fl_pid = 0; + break; + default: + status = nlm_stat_to_errno(req->a_res.status); } - - return 0; +out: + nlm_release_call(req); + return status; } static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl) @@ -560,7 +530,7 @@ out_unblock: if (resp->status == NLM_LCK_BLOCKED) nlmclnt_cancel(host, req->a_args.block, fl); out: - nlmclnt_release_lockargs(req); + nlm_release_call(req); return status; } @@ -623,32 +593,24 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) */ do_vfs_lock(fl); - if (req->a_flags & RPC_TASK_ASYNC) { - status = nlmclnt_async_call(req, NLMPROC_UNLOCK, - &nlmclnt_unlock_ops); - /* Hrmf... Do the unlock early since locks_remove_posix() - * really expects us to free the lock synchronously */ - if (status < 0) { - nlmclnt_release_lockargs(req); - kfree(req); - } - return status; - } + if (req->a_flags & RPC_TASK_ASYNC) + return nlm_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops); status = nlmclnt_call(req, NLMPROC_UNLOCK); - nlmclnt_release_lockargs(req); if (status < 0) - return status; + goto out; + status = 0; if (resp->status == NLM_LCK_GRANTED) - return 0; + goto out; if (resp->status != NLM_LCK_DENIED_NOLOCKS) printk("lockd: unexpected unlock status: %d\n", resp->status); - /* What to do now? I'm out of my depth... */ - - return -ENOLCK; + status = -ENOLCK; +out: + nlm_release_call(req); + return status; } static void nlmclnt_unlock_callback(struct rpc_task *task, void *data) @@ -670,9 +632,6 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data) if (status != NLM_LCK_GRANTED) printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status); die: - nlm_release_host(req->a_host); - nlmclnt_release_lockargs(req); - kfree(req); return; retry_rebind: nlm_rebind_host(req->a_host); @@ -682,6 +641,7 @@ die: static const struct rpc_call_ops nlmclnt_unlock_ops = { .rpc_call_done = nlmclnt_unlock_callback, + .rpc_release = nlmclnt_rpc_release, }; /* @@ -703,20 +663,15 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, flags); - req = nlmclnt_alloc_call(); + req = nlm_alloc_call(nlm_get_host(host)); if (!req) return -ENOMEM; - req->a_host = host; req->a_flags = RPC_TASK_ASYNC; nlmclnt_setlockargs(req, fl); req->a_args.block = block; - status = nlmclnt_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops); - if (status < 0) { - nlmclnt_release_lockargs(req); - kfree(req); - } + status = nlm_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops); spin_lock_irqsave(¤t->sighand->siglock, flags); current->blocked = oldset; @@ -757,9 +712,6 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data) } die: - nlm_release_host(req->a_host); - nlmclnt_release_lockargs(req); - kfree(req); return; retry_cancel: @@ -773,6 +725,7 @@ retry_cancel: static const struct rpc_call_ops nlmclnt_cancel_ops = { .rpc_call_done = nlmclnt_cancel_callback, + .rpc_release = nlmclnt_rpc_release, }; /* diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index ac4a700af01a..cb51c7025825 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -480,43 +480,37 @@ nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp) struct nlm_host *host; struct nlm_rqst *call; - if (!(call = nlmclnt_alloc_call())) + host = nlmsvc_lookup_host(rqstp); + if (host == NULL) return rpc_system_err; - host = nlmsvc_lookup_host(rqstp); - if (!host) { - kfree(call); + call = nlm_alloc_call(host); + if (call == NULL) return rpc_system_err; - } + call->a_flags = RPC_TASK_ASYNC; - call->a_host = host; memcpy(&call->a_args, resp, sizeof(*resp)); - if (nlmsvc_async_call(call, proc, &nlm4svc_callback_ops) < 0) - goto error; - + if (nlm_async_call(call, proc, &nlm4svc_callback_ops) < 0) + return rpc_system_err; return rpc_success; - error: - kfree(call); - nlm_release_host(host); - return rpc_system_err; } static void nlm4svc_callback_exit(struct rpc_task *task, void *data) { - struct nlm_rqst *call = data; + dprintk("lockd: %4d callback returned %d\n", task->tk_pid, + -task->tk_status); +} - if (task->tk_status < 0) { - dprintk("lockd: %4d callback failed (errno = %d)\n", - task->tk_pid, -task->tk_status); - } - nlm_release_host(call->a_host); - kfree(call); +static void nlm4svc_callback_release(void *data) +{ + nlm_release_call(data); } static const struct rpc_call_ops nlm4svc_callback_ops = { .rpc_call_done = nlm4svc_callback_exit, + .rpc_release = nlm4svc_callback_release, }; /* diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index a95d260b7137..185bf7ea1c0c 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -117,12 +117,12 @@ nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove) (long long)lock->fl.fl_start, (long long)lock->fl.fl_end, lock->fl.fl_type); for (head = &nlm_blocked; (block = *head) != 0; head = &block->b_next) { - fl = &block->b_call.a_args.lock.fl; + fl = &block->b_call->a_args.lock.fl; dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n", block->b_file, fl->fl_pid, (long long)fl->fl_start, (long long)fl->fl_end, fl->fl_type, - nlmdbg_cookie2a(&block->b_call.a_args.cookie)); + nlmdbg_cookie2a(&block->b_call->a_args.cookie)); if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) { if (remove) { *head = block->b_next; @@ -156,7 +156,7 @@ nlmsvc_find_block(struct nlm_cookie *cookie, struct sockaddr_in *sin) for (block = nlm_blocked; block; block = block->b_next) { dprintk("cookie: head of blocked queue %p, block %p\n", nlm_blocked, block); - if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie) + if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie) && nlm_cmp_addr(sin, &block->b_host->h_addr)) break; } @@ -182,28 +182,30 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, { struct nlm_block *block; struct nlm_host *host; - struct nlm_rqst *call; + struct nlm_rqst *call = NULL; /* Create host handle for callback */ host = nlmsvc_lookup_host(rqstp); if (host == NULL) return NULL; + call = nlm_alloc_call(host); + if (call == NULL) + return NULL; + /* Allocate memory for block, and initialize arguments */ - if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL))) + block = kzalloc(sizeof(*block), GFP_KERNEL); + if (block == NULL) goto failed; - memset(block, 0, sizeof(*block)); - locks_init_lock(&block->b_call.a_args.lock.fl); - locks_init_lock(&block->b_call.a_res.lock.fl); kref_init(&block->b_count); - if (!nlmsvc_setgrantargs(&block->b_call, lock)) + if (!nlmsvc_setgrantargs(call, lock)) goto failed_free; /* Set notifier function for VFS, and init args */ - block->b_call.a_args.lock.fl.fl_flags |= FL_SLEEP; - block->b_call.a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations; - block->b_call.a_args.cookie = *cookie; /* see above */ + call->a_args.lock.fl.fl_flags |= FL_SLEEP; + call->a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations; + call->a_args.cookie = *cookie; /* see above */ dprintk("lockd: created block %p...\n", block); @@ -217,16 +219,16 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, file->f_blocks = block; /* Set up RPC arguments for callback */ - call = &block->b_call; - call->a_host = host; + block->b_call = call; call->a_flags = RPC_TASK_ASYNC; + call->a_block = block; return block; failed_free: kfree(block); failed: - nlm_release_host(host); + nlm_release_call(call); return NULL; } @@ -242,7 +244,7 @@ static int nlmsvc_unlink_block(struct nlm_block *block) dprintk("lockd: unlinking block %p...\n", block); /* Remove block from list */ - status = posix_unblock_lock(block->b_file->f_file, &block->b_call.a_args.lock.fl); + status = posix_unblock_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl); nlmsvc_remove_block(block); return status; } @@ -263,9 +265,8 @@ static void nlmsvc_free_block(struct kref *kref) } } - if (block->b_host) - nlm_release_host(block->b_host); - nlmsvc_freegrantargs(&block->b_call); + nlmsvc_freegrantargs(block->b_call); + nlm_release_call(block->b_call); kfree(block); } @@ -316,10 +317,8 @@ static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) if (lock->oh.len > NLMCLNT_OHSIZE) { void *data = kmalloc(lock->oh.len, GFP_KERNEL); - if (!data) { - nlmsvc_freegrantargs(call); + if (!data) return 0; - } call->a_args.lock.oh.data = (u8 *) data; } @@ -329,17 +328,8 @@ static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) static void nlmsvc_freegrantargs(struct nlm_rqst *call) { - struct file_lock *fl = &call->a_args.lock.fl; - /* - * Check whether we allocated memory for the owner. - */ - if (call->a_args.lock.oh.data != (u8 *) call->a_owner) { + if (call->a_args.lock.oh.data != call->a_owner) kfree(call->a_args.lock.oh.data); - } - if (fl->fl_ops && fl->fl_ops->fl_release_private) - fl->fl_ops->fl_release_private(fl); - if (fl->fl_lmops && fl->fl_lmops->fl_release_private) - fl->fl_lmops->fl_release_private(fl); } /* @@ -371,9 +361,9 @@ again: block = nlmsvc_lookup_block(file, lock, 0); if (block == NULL) { if (newblock != NULL) - lock = &newblock->b_call.a_args.lock; + lock = &newblock->b_call->a_args.lock; } else - lock = &block->b_call.a_args.lock; + lock = &block->b_call->a_args.lock; error = posix_lock_file(file->f_file, &lock->fl); lock->fl.fl_flags &= ~FL_SLEEP; @@ -523,7 +513,7 @@ nlmsvc_notify_blocked(struct file_lock *fl) dprintk("lockd: VFS unblock notification for block %p\n", fl); for (bp = &nlm_blocked; (block = *bp) != 0; bp = &block->b_next) { - if (nlm_compare_locks(&block->b_call.a_args.lock.fl, fl)) { + if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) { nlmsvc_insert_block(block, 0); svc_wake_up(block->b_daemon); return; @@ -558,7 +548,7 @@ static void nlmsvc_grant_blocked(struct nlm_block *block) { struct nlm_file *file = block->b_file; - struct nlm_lock *lock = &block->b_call.a_args.lock; + struct nlm_lock *lock = &block->b_call->a_args.lock; int error; dprintk("lockd: grant blocked lock %p\n", block); @@ -606,7 +596,7 @@ callback: /* Call the client */ kref_get(&block->b_count); - if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG, + if (nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG, &nlmsvc_grant_ops) < 0) nlmsvc_release_block(block); out_unlock: @@ -624,7 +614,7 @@ out_unlock: static void nlmsvc_grant_callback(struct rpc_task *task, void *data) { struct nlm_rqst *call = data; - struct nlm_block *block = container_of(call, struct nlm_block, b_call); + struct nlm_block *block = call->a_block; unsigned long timeout; dprintk("lockd: GRANT_MSG RPC callback\n"); diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 4986fbe44540..956d1d71e2af 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -505,43 +505,36 @@ nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp) struct nlm_host *host; struct nlm_rqst *call; - if (!(call = nlmclnt_alloc_call())) + host = nlmsvc_lookup_host(rqstp); + if (host == NULL) return rpc_system_err; - host = nlmsvc_lookup_host(rqstp); - if (!host) { - kfree(call); + call = nlm_alloc_call(host); + if (call == NULL) return rpc_system_err; - } call->a_flags = RPC_TASK_ASYNC; - call->a_host = host; memcpy(&call->a_args, resp, sizeof(*resp)); - if (nlmsvc_async_call(call, proc, &nlmsvc_callback_ops) < 0) - goto error; - + if (nlm_async_call(call, proc, &nlmsvc_callback_ops) < 0) + return rpc_system_err; return rpc_success; - error: - nlm_release_host(host); - kfree(call); - return rpc_system_err; } static void nlmsvc_callback_exit(struct rpc_task *task, void *data) { - struct nlm_rqst *call = data; + dprintk("lockd: %4d callback returned %d\n", task->tk_pid, + -task->tk_status); +} - if (task->tk_status < 0) { - dprintk("lockd: %4d callback failed (errno = %d)\n", - task->tk_pid, -task->tk_status); - } - nlm_release_host(call->a_host); - kfree(call); +static void nlmsvc_callback_release(void *data) +{ + nlm_release_call(data); } static const struct rpc_call_ops nlmsvc_callback_ops = { .rpc_call_done = nlmsvc_callback_exit, + .rpc_release = nlmsvc_callback_release, }; /* diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index cb9933d04091..e7ba8110d579 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -86,8 +86,9 @@ struct nlm_rqst { struct nlm_host * a_host; /* host handle */ struct nlm_args a_args; /* arguments */ struct nlm_res a_res; /* result */ + struct nlm_block * a_block; unsigned int a_retries; /* Retry count */ - char a_owner[NLMCLNT_OHSIZE]; + u8 a_owner[NLMCLNT_OHSIZE]; }; /* @@ -115,7 +116,7 @@ struct nlm_block { struct kref b_count; /* Reference count */ struct nlm_block * b_next; /* linked list (all blocks) */ struct nlm_block * b_fnext; /* linked list (per file) */ - struct nlm_rqst b_call; /* RPC args & callback info */ + struct nlm_rqst * b_call; /* RPC args & callback info */ struct svc_serv * b_daemon; /* NLM service */ struct nlm_host * b_host; /* host handle for RPC clnt */ unsigned long b_when; /* next re-xmit */ @@ -147,7 +148,9 @@ extern unsigned long nlmsvc_timeout; /* * Lockd client functions */ -struct nlm_rqst * nlmclnt_alloc_call(void); +struct nlm_rqst * nlm_alloc_call(struct nlm_host *host); +void nlm_release_call(struct nlm_rqst *); +int nlm_async_call(struct nlm_rqst *, u32, const struct rpc_call_ops *); struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl); void nlmclnt_finish_block(struct nlm_wait *block); int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout); @@ -172,7 +175,6 @@ extern struct nlm_host *nlm_find_client(void); /* * Server-side lock handling */ -int nlmsvc_async_call(struct nlm_rqst *, u32, const struct rpc_call_ops *); u32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_lock *, int, struct nlm_cookie *); u32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); -- cgit v1.2.3 From d47166244860eb5dfdb12ee4703968beef8a0db2 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:45 -0500 Subject: lockd: Add helper for *_RES callbacks Signed-off-by: Trond Myklebust --- fs/lockd/clntproc.c | 27 +++++--- fs/lockd/svc4proc.c | 150 ++++++++++++++++---------------------------- fs/lockd/svcproc.c | 143 ++++++++++++++++------------------------- include/linux/lockd/lockd.h | 1 + 4 files changed, 129 insertions(+), 192 deletions(-) (limited to 'include') diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 3f8ad7c54efa..f96e38155b5c 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -354,14 +354,10 @@ in_grace_period: /* * Generic NLM call, async version. */ -int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) +static int __nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) { struct nlm_host *host = req->a_host; struct rpc_clnt *clnt; - struct rpc_message msg = { - .rpc_argp = &req->a_args, - .rpc_resp = &req->a_res, - }; int status = -ENOLCK; dprintk("lockd: call procedure %d on %s (async)\n", @@ -371,10 +367,10 @@ int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk clnt = nlm_bind_host(host); if (clnt == NULL) goto out_err; - msg.rpc_proc = &clnt->cl_procinfo[proc]; + msg->rpc_proc = &clnt->cl_procinfo[proc]; /* bootstrap and kick off the async RPC call */ - status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, tk_ops, req); + status = rpc_call_async(clnt, msg, RPC_TASK_ASYNC, tk_ops, req); if (status == 0) return 0; out_err: @@ -382,6 +378,23 @@ out_err: return status; } +int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) +{ + struct rpc_message msg = { + .rpc_argp = &req->a_args, + .rpc_resp = &req->a_res, + }; + return __nlm_async_call(req, proc, &msg, tk_ops); +} + +int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) +{ + struct rpc_message msg = { + .rpc_argp = &req->a_res, + }; + return __nlm_async_call(req, proc, &msg, tk_ops); +} + /* * TEST for the presence of a conflicting lock */ diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index cb51c7025825..a2dd9ccb9b32 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -21,10 +21,6 @@ #define NLMDBG_FACILITY NLMDBG_CLIENT -static u32 nlm4svc_callback(struct svc_rqst *, u32, struct nlm_res *); - -static const struct rpc_call_ops nlm4svc_callback_ops; - /* * Obtain client and file from arguments */ @@ -233,84 +229,90 @@ nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp, return rpc_success; } +/* + * This is the generic lockd callback for async RPC calls + */ +static void nlm4svc_callback_exit(struct rpc_task *task, void *data) +{ + dprintk("lockd: %4d callback returned %d\n", task->tk_pid, + -task->tk_status); +} + +static void nlm4svc_callback_release(void *data) +{ + nlm_release_call(data); +} + +static const struct rpc_call_ops nlm4svc_callback_ops = { + .rpc_call_done = nlm4svc_callback_exit, + .rpc_release = nlm4svc_callback_release, +}; + /* * `Async' versions of the above service routines. They aren't really, * because we send the callback before the reply proper. I hope this * doesn't break any clients. */ -static int -nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp, - void *resp) +static int nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args *argp, + int (*func)(struct svc_rqst *, struct nlm_args *, struct nlm_res *)) { - struct nlm_res res; - u32 stat; + struct nlm_host *host; + struct nlm_rqst *call; + int stat; - dprintk("lockd: TEST_MSG called\n"); - memset(&res, 0, sizeof(res)); + host = nlmsvc_lookup_host(rqstp); + if (host == NULL) + return rpc_system_err; + + call = nlm_alloc_call(host); + if (call == NULL) + return rpc_system_err; + + stat = func(rqstp, argp, &call->a_res); + if (stat != 0) { + nlm_release_call(call); + return stat; + } - if ((stat = nlm4svc_proc_test(rqstp, argp, &res)) == 0) - stat = nlm4svc_callback(rqstp, NLMPROC_TEST_RES, &res); - return stat; + call->a_flags = RPC_TASK_ASYNC; + if (nlm_async_reply(call, proc, &nlm4svc_callback_ops) < 0) + return rpc_system_err; + return rpc_success; } -static int -nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, +static int nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp, void *resp) { - struct nlm_res res; - u32 stat; + dprintk("lockd: TEST_MSG called\n"); + return nlm4svc_callback(rqstp, NLMPROC_TEST_RES, argp, nlm4svc_proc_test); +} +static int nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ dprintk("lockd: LOCK_MSG called\n"); - memset(&res, 0, sizeof(res)); - - if ((stat = nlm4svc_proc_lock(rqstp, argp, &res)) == 0) - stat = nlm4svc_callback(rqstp, NLMPROC_LOCK_RES, &res); - return stat; + return nlm4svc_callback(rqstp, NLMPROC_LOCK_RES, argp, nlm4svc_proc_lock); } -static int -nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp, +static int nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp, void *resp) { - struct nlm_res res; - u32 stat; - dprintk("lockd: CANCEL_MSG called\n"); - memset(&res, 0, sizeof(res)); - - if ((stat = nlm4svc_proc_cancel(rqstp, argp, &res)) == 0) - stat = nlm4svc_callback(rqstp, NLMPROC_CANCEL_RES, &res); - return stat; + return nlm4svc_callback(rqstp, NLMPROC_CANCEL_RES, argp, nlm4svc_proc_cancel); } -static int -nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, +static int nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, void *resp) { - struct nlm_res res; - u32 stat; - dprintk("lockd: UNLOCK_MSG called\n"); - memset(&res, 0, sizeof(res)); - - if ((stat = nlm4svc_proc_unlock(rqstp, argp, &res)) == 0) - stat = nlm4svc_callback(rqstp, NLMPROC_UNLOCK_RES, &res); - return stat; + return nlm4svc_callback(rqstp, NLMPROC_UNLOCK_RES, argp, nlm4svc_proc_unlock); } -static int -nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp, +static int nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp, void *resp) { - struct nlm_res res; - u32 stat; - dprintk("lockd: GRANTED_MSG called\n"); - memset(&res, 0, sizeof(res)); - - if ((stat = nlm4svc_proc_granted(rqstp, argp, &res)) == 0) - stat = nlm4svc_callback(rqstp, NLMPROC_GRANTED_RES, &res); - return stat; + return nlm4svc_callback(rqstp, NLMPROC_GRANTED_RES, argp, nlm4svc_proc_granted); } /* @@ -471,48 +473,6 @@ nlm4svc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp, } -/* - * This is the generic lockd callback for async RPC calls - */ -static u32 -nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp) -{ - struct nlm_host *host; - struct nlm_rqst *call; - - host = nlmsvc_lookup_host(rqstp); - if (host == NULL) - return rpc_system_err; - - call = nlm_alloc_call(host); - if (call == NULL) - return rpc_system_err; - - - call->a_flags = RPC_TASK_ASYNC; - memcpy(&call->a_args, resp, sizeof(*resp)); - - if (nlm_async_call(call, proc, &nlm4svc_callback_ops) < 0) - return rpc_system_err; - return rpc_success; -} - -static void nlm4svc_callback_exit(struct rpc_task *task, void *data) -{ - dprintk("lockd: %4d callback returned %d\n", task->tk_pid, - -task->tk_status); -} - -static void nlm4svc_callback_release(void *data) -{ - nlm_release_call(data); -} - -static const struct rpc_call_ops nlm4svc_callback_ops = { - .rpc_call_done = nlm4svc_callback_exit, - .rpc_release = nlm4svc_callback_release, -}; - /* * NLM Server procedures. */ diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 956d1d71e2af..d210cf304e92 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -22,10 +22,6 @@ #define NLMDBG_FACILITY NLMDBG_CLIENT -static u32 nlmsvc_callback(struct svc_rqst *, u32, struct nlm_res *); - -static const struct rpc_call_ops nlmsvc_callback_ops; - #ifdef CONFIG_LOCKD_V4 static u32 cast_to_nlm(u32 status, u32 vers) @@ -261,84 +257,92 @@ nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp, return rpc_success; } +/* + * This is the generic lockd callback for async RPC calls + */ +static void nlmsvc_callback_exit(struct rpc_task *task, void *data) +{ + dprintk("lockd: %4d callback returned %d\n", task->tk_pid, + -task->tk_status); +} + +static void nlmsvc_callback_release(void *data) +{ + nlm_release_call(data); +} + +static const struct rpc_call_ops nlmsvc_callback_ops = { + .rpc_call_done = nlmsvc_callback_exit, + .rpc_release = nlmsvc_callback_release, +}; + /* * `Async' versions of the above service routines. They aren't really, * because we send the callback before the reply proper. I hope this * doesn't break any clients. */ -static int -nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp, - void *resp) +static int nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args *argp, + int (*func)(struct svc_rqst *, struct nlm_args *, struct nlm_res *)) { - struct nlm_res res; - u32 stat; + struct nlm_host *host; + struct nlm_rqst *call; + int stat; - dprintk("lockd: TEST_MSG called\n"); - memset(&res, 0, sizeof(res)); + host = nlmsvc_lookup_host(rqstp); + if (host == NULL) + return rpc_system_err; + + call = nlm_alloc_call(host); + if (call == NULL) + return rpc_system_err; + + stat = func(rqstp, argp, &call->a_res); + if (stat != 0) { + nlm_release_call(call); + return stat; + } - if ((stat = nlmsvc_proc_test(rqstp, argp, &res)) == 0) - stat = nlmsvc_callback(rqstp, NLMPROC_TEST_RES, &res); - return stat; + call->a_flags = RPC_TASK_ASYNC; + if (nlm_async_reply(call, proc, &nlmsvc_callback_ops) < 0) + return rpc_system_err; + return rpc_success; } -static int -nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, +static int nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp, void *resp) { - struct nlm_res res; - u32 stat; + dprintk("lockd: TEST_MSG called\n"); + return nlmsvc_callback(rqstp, NLMPROC_TEST_RES, argp, nlmsvc_proc_test); +} +static int nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ dprintk("lockd: LOCK_MSG called\n"); - memset(&res, 0, sizeof(res)); - - if ((stat = nlmsvc_proc_lock(rqstp, argp, &res)) == 0) - stat = nlmsvc_callback(rqstp, NLMPROC_LOCK_RES, &res); - return stat; + return nlmsvc_callback(rqstp, NLMPROC_LOCK_RES, argp, nlmsvc_proc_lock); } -static int -nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp, +static int nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp, void *resp) { - struct nlm_res res; - u32 stat; - dprintk("lockd: CANCEL_MSG called\n"); - memset(&res, 0, sizeof(res)); - - if ((stat = nlmsvc_proc_cancel(rqstp, argp, &res)) == 0) - stat = nlmsvc_callback(rqstp, NLMPROC_CANCEL_RES, &res); - return stat; + return nlmsvc_callback(rqstp, NLMPROC_CANCEL_RES, argp, nlmsvc_proc_cancel); } static int nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, void *resp) { - struct nlm_res res; - u32 stat; - dprintk("lockd: UNLOCK_MSG called\n"); - memset(&res, 0, sizeof(res)); - - if ((stat = nlmsvc_proc_unlock(rqstp, argp, &res)) == 0) - stat = nlmsvc_callback(rqstp, NLMPROC_UNLOCK_RES, &res); - return stat; + return nlmsvc_callback(rqstp, NLMPROC_UNLOCK_RES, argp, nlmsvc_proc_unlock); } static int nlmsvc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp, void *resp) { - struct nlm_res res; - u32 stat; - dprintk("lockd: GRANTED_MSG called\n"); - memset(&res, 0, sizeof(res)); - - if ((stat = nlmsvc_proc_granted(rqstp, argp, &res)) == 0) - stat = nlmsvc_callback(rqstp, NLMPROC_GRANTED_RES, &res); - return stat; + return nlmsvc_callback(rqstp, NLMPROC_GRANTED_RES, argp, nlmsvc_proc_granted); } /* @@ -496,47 +500,6 @@ nlmsvc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp, return rpc_success; } -/* - * This is the generic lockd callback for async RPC calls - */ -static u32 -nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp) -{ - struct nlm_host *host; - struct nlm_rqst *call; - - host = nlmsvc_lookup_host(rqstp); - if (host == NULL) - return rpc_system_err; - - call = nlm_alloc_call(host); - if (call == NULL) - return rpc_system_err; - - call->a_flags = RPC_TASK_ASYNC; - memcpy(&call->a_args, resp, sizeof(*resp)); - - if (nlm_async_call(call, proc, &nlmsvc_callback_ops) < 0) - return rpc_system_err; - return rpc_success; -} - -static void nlmsvc_callback_exit(struct rpc_task *task, void *data) -{ - dprintk("lockd: %4d callback returned %d\n", task->tk_pid, - -task->tk_status); -} - -static void nlmsvc_callback_release(void *data) -{ - nlm_release_call(data); -} - -static const struct rpc_call_ops nlmsvc_callback_ops = { - .rpc_call_done = nlmsvc_callback_exit, - .rpc_release = nlmsvc_callback_release, -}; - /* * NLM Server procedures. */ diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index e7ba8110d579..a04137d0c5de 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -151,6 +151,7 @@ extern unsigned long nlmsvc_timeout; struct nlm_rqst * nlm_alloc_call(struct nlm_host *host); void nlm_release_call(struct nlm_rqst *); int nlm_async_call(struct nlm_rqst *, u32, const struct rpc_call_ops *); +int nlm_async_reply(struct nlm_rqst *, u32, const struct rpc_call_ops *); struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl); void nlmclnt_finish_block(struct nlm_wait *block); int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout); -- cgit v1.2.3 From 5428154827c2bf7cfdc9dab60db1e0eaa57c027a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:49 -0500 Subject: SUNRPC: Fix a 'Busy inodes' error in rpc_pipefs Signed-off-by: Trond Myklebust --- include/linux/sunrpc/clnt.h | 1 + include/linux/sunrpc/rpc_pipe_fs.h | 2 ++ net/sunrpc/clnt.c | 24 ++++++++++++++++++++---- net/sunrpc/rpc_pipe.c | 22 ++++++++++++---------- 4 files changed, 35 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index e37c06128e51..8fe9f35eba31 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -60,6 +60,7 @@ struct rpc_clnt { int cl_nodelen; /* nodename length */ char cl_nodename[UNX_MAXNODENAME]; char cl_pathname[30];/* Path in rpc_pipe_fs */ + struct vfsmount * cl_vfsmnt; struct dentry * cl_dentry; /* inode */ struct rpc_clnt * cl_parent; /* Points to parent of clones */ struct rpc_rtt cl_rtt_default; diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index 63929349571f..2c2189cb30aa 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -45,6 +45,8 @@ extern struct dentry *rpc_mkdir(char *, struct rpc_clnt *); extern int rpc_rmdir(char *); extern struct dentry *rpc_mkpipe(char *, void *, struct rpc_pipe_ops *, int flags); extern int rpc_unlink(char *); +extern struct vfsmount *rpc_get_mount(void); +extern void rpc_put_mount(void); #endif #endif diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 0bb23e8e9d0c..9f775302d1df 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -70,8 +70,15 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) static uint32_t clntid; int error; + clnt->cl_vfsmnt = ERR_PTR(-ENOENT); + clnt->cl_dentry = ERR_PTR(-ENOENT); if (dir_name == NULL) return 0; + + clnt->cl_vfsmnt = rpc_get_mount(); + if (IS_ERR(clnt->cl_vfsmnt)) + return PTR_ERR(clnt->cl_vfsmnt); + for (;;) { snprintf(clnt->cl_pathname, sizeof(clnt->cl_pathname), "%s/clnt%x", dir_name, @@ -84,6 +91,7 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) if (error != -EEXIST) { printk(KERN_INFO "RPC: Couldn't create pipefs entry %s, error %d\n", clnt->cl_pathname, error); + rpc_put_mount(); return error; } } @@ -175,7 +183,11 @@ rpc_new_client(struct rpc_xprt *xprt, char *servname, return clnt; out_no_auth: - rpc_rmdir(clnt->cl_pathname); + if (!IS_ERR(clnt->cl_dentry)) { + rpc_rmdir(clnt->cl_pathname); + dput(clnt->cl_dentry); + rpc_put_mount(); + } out_no_path: if (clnt->cl_server != clnt->cl_inline_name) kfree(clnt->cl_server); @@ -240,13 +252,15 @@ rpc_clone_client(struct rpc_clnt *clnt) new->cl_autobind = 0; new->cl_oneshot = 0; new->cl_dead = 0; - dget(new->cl_dentry); + if (!IS_ERR(new->cl_dentry)) { + dget(new->cl_dentry); + rpc_get_mount(); + } rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval); if (new->cl_auth) atomic_inc(&new->cl_auth->au_count); new->cl_pmap = &new->cl_pmap_default; new->cl_metrics = rpc_alloc_iostats(clnt); - rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait"); return new; out_no_clnt: printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__); @@ -318,8 +332,10 @@ rpc_destroy_client(struct rpc_clnt *clnt) out_free: rpc_free_iostats(clnt->cl_metrics); clnt->cl_metrics = NULL; - if (clnt->cl_dentry) + if (!IS_ERR(clnt->cl_dentry)) { dput(clnt->cl_dentry); + rpc_put_mount(); + } kfree(clnt); return 0; } diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 72b22172f0af..391d2bfc71aa 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -435,14 +435,17 @@ static struct rpc_filelist authfiles[] = { }, }; -static int -rpc_get_mount(void) +struct vfsmount *rpc_get_mount(void) { - return simple_pin_fs("rpc_pipefs", &rpc_mount, &rpc_mount_count); + int err; + + err = simple_pin_fs("rpc_pipefs", &rpc_mount, &rpc_mount_count); + if (err != 0) + return ERR_PTR(err); + return rpc_mount; } -static void -rpc_put_mount(void) +void rpc_put_mount(void) { simple_release_fs(&rpc_mount, &rpc_mount_count); } @@ -452,12 +455,13 @@ rpc_lookup_parent(char *path, struct nameidata *nd) { if (path[0] == '\0') return -ENOENT; - if (rpc_get_mount()) { + nd->mnt = rpc_get_mount(); + if (IS_ERR(nd->mnt)) { printk(KERN_WARNING "%s: %s failed to mount " "pseudofilesystem \n", __FILE__, __FUNCTION__); - return -ENODEV; + return PTR_ERR(nd->mnt); } - nd->mnt = mntget(rpc_mount); + mntget(nd->mnt); nd->dentry = dget(rpc_mount->mnt_root); nd->last_type = LAST_ROOT; nd->flags = LOOKUP_PARENT; @@ -594,7 +598,6 @@ __rpc_mkdir(struct inode *dir, struct dentry *dentry) d_instantiate(dentry, inode); dir->i_nlink++; inode_dir_notify(dir, DN_CREATE); - rpc_get_mount(); return 0; out_err: printk(KERN_WARNING "%s: %s failed to allocate inode for dentry %s\n", @@ -615,7 +618,6 @@ __rpc_rmdir(struct inode *dir, struct dentry *dentry) if (!error) { inode_dir_notify(dir, DN_DELETE); d_drop(dentry); - rpc_put_mount(); } return 0; } -- cgit v1.2.3 From c42de9dd67250fe984e0e31c9b542d721af6454b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 Mar 2006 13:44:51 -0500 Subject: NFS: Fix a race in nfs_sync_inode() Kudos to Neil Brown for spotting the problem: "in nfs_sync_inode, there is effectively the sequence: nfs_wait_on_requests nfs_flush_inode nfs_commit_inode This seems a bit racy to me as if the only requests are on the ->commit list, and nfs_commit_inode is called separately after nfs_wait_on_requests completes, and before nfs_commit_inode start (say: by nfs_write_inode) then none of these function will return >0, yet there will be some pending request that aren't waited for." The solution is to search for requests to wait upon, search for dirty requests, and search for uncommitted requests while holding the nfsi->req_lock The patch also cleans up nfs_sync_inode(), getting rid of the redundant FLUSH_WAIT flag. It turns out that we were always setting it. Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 4 +-- fs/nfs/write.c | 72 +++++++++++++++++++++++++++++++++++--------------- include/linux/nfs_fs.h | 10 +++---- 3 files changed, 56 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index a0cda53461b3..60aac58270a8 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -137,7 +137,7 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr) static int nfs_write_inode(struct inode *inode, int sync) { - int flags = sync ? FLUSH_WAIT : 0; + int flags = sync ? FLUSH_SYNC : 0; int ret; ret = nfs_commit_inode(inode, flags); @@ -1051,7 +1051,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) int err; /* Flush out writes to the server in order to update c/mtime */ - nfs_sync_inode(inode, 0, 0, FLUSH_WAIT|FLUSH_NOCOMMIT); + nfs_sync_inode_wait(inode, 0, 0, FLUSH_NOCOMMIT); /* * We may force a getattr if the user cares about atime. diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 2beb1268bb1b..3f5225404c97 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -539,8 +539,7 @@ nfs_mark_request_commit(struct nfs_page *req) * * Interruptible by signals only if mounted with intr flag. */ -static int -nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int npages) +static int nfs_wait_on_requests_locked(struct inode *inode, unsigned long idx_start, unsigned int npages) { struct nfs_inode *nfsi = NFS_I(inode); struct nfs_page *req; @@ -553,7 +552,6 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int else idx_end = idx_start + npages - 1; - spin_lock(&nfsi->req_lock); next = idx_start; while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_WRITEBACK)) { if (req->wb_index > idx_end) @@ -566,15 +564,25 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int spin_unlock(&nfsi->req_lock); error = nfs_wait_on_request(req); nfs_release_request(req); + spin_lock(&nfsi->req_lock); if (error < 0) return error; - spin_lock(&nfsi->req_lock); res++; } - spin_unlock(&nfsi->req_lock); return res; } +static int nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int npages) +{ + struct nfs_inode *nfsi = NFS_I(inode); + int ret; + + spin_lock(&nfsi->req_lock); + ret = nfs_wait_on_requests_locked(inode, idx_start, npages); + spin_unlock(&nfsi->req_lock); + return ret; +} + /* * nfs_scan_dirty - Scan an inode for dirty requests * @inode: NFS inode to scan @@ -626,6 +634,11 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_st } return res; } +#else +static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) +{ + return 0; +} #endif static int nfs_wait_on_write_congestion(struct address_space *mapping, int intr) @@ -1421,6 +1434,11 @@ static const struct rpc_call_ops nfs_commit_ops = { .rpc_call_done = nfs_commit_done, .rpc_release = nfs_commit_release, }; +#else +static inline int nfs_commit_list(struct inode *inode, struct list_head *head, int how) +{ + return 0; +} #endif static int nfs_flush_inode(struct inode *inode, unsigned long idx_start, @@ -1460,28 +1478,38 @@ int nfs_commit_inode(struct inode *inode, int how) } #endif -int nfs_sync_inode(struct inode *inode, unsigned long idx_start, - unsigned int npages, int how) +int nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start, + unsigned int npages, int how) { + struct nfs_inode *nfsi = NFS_I(inode); + LIST_HEAD(head); int nocommit = how & FLUSH_NOCOMMIT; - int wait = how & FLUSH_WAIT; - int error; - - how &= ~(FLUSH_WAIT|FLUSH_NOCOMMIT); + int pages, ret; + how &= ~FLUSH_NOCOMMIT; + spin_lock(&nfsi->req_lock); do { - if (wait) { - error = nfs_wait_on_requests(inode, idx_start, npages); - if (error != 0) - continue; - } - error = nfs_flush_inode(inode, idx_start, npages, how); - if (error != 0) + ret = nfs_wait_on_requests_locked(inode, idx_start, npages); + if (ret != 0) continue; - if (!nocommit) - error = nfs_commit_inode(inode, how); - } while (error > 0); - return error; + pages = nfs_scan_dirty(inode, &head, idx_start, npages); + if (pages != 0) { + spin_unlock(&nfsi->req_lock); + ret = nfs_flush_list(inode, &head, pages, how); + spin_lock(&nfsi->req_lock); + continue; + } + if (nocommit) + break; + pages = nfs_scan_commit(inode, &head, 0, 0); + if (pages == 0) + break; + spin_unlock(&nfsi->req_lock); + ret = nfs_commit_list(inode, &head, how); + spin_lock(&nfsi->req_lock); + } while (ret >= 0); + spin_unlock(&nfsi->req_lock); + return ret; } int nfs_init_writepagecache(void) diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 55de0770df4a..cbebd7d1b9e8 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -56,9 +56,7 @@ * When flushing a cluster of dirty pages, there can be different * strategies: */ -#define FLUSH_AGING 0 /* only flush old buffers */ #define FLUSH_SYNC 1 /* file being synced, or contention */ -#define FLUSH_WAIT 2 /* wait for completion */ #define FLUSH_STABLE 4 /* commit to stable storage */ #define FLUSH_LOWPRI 8 /* low priority background flush */ #define FLUSH_HIGHPRI 16 /* high priority memory reclaim flush */ @@ -419,7 +417,7 @@ void nfs_commit_free(struct nfs_write_data *p); * Try to write back everything synchronously (but check the * return value!) */ -extern int nfs_sync_inode(struct inode *, unsigned long, unsigned int, int); +extern int nfs_sync_inode_wait(struct inode *, unsigned long, unsigned int, int); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) extern int nfs_commit_inode(struct inode *, int); extern void nfs_commit_release(void *wdata); @@ -440,7 +438,7 @@ nfs_have_writebacks(struct inode *inode) static inline int nfs_wb_all(struct inode *inode) { - int error = nfs_sync_inode(inode, 0, 0, FLUSH_WAIT); + int error = nfs_sync_inode_wait(inode, 0, 0, 0); return (error < 0) ? error : 0; } @@ -449,8 +447,8 @@ nfs_wb_all(struct inode *inode) */ static inline int nfs_wb_page_priority(struct inode *inode, struct page* page, int how) { - int error = nfs_sync_inode(inode, page->index, 1, - how | FLUSH_WAIT | FLUSH_STABLE); + int error = nfs_sync_inode_wait(inode, page->index, 1, + how | FLUSH_STABLE); return (error < 0) ? error : 0; } -- cgit v1.2.3 From b63862f46547487388e582e8ac9083830d34f058 Mon Sep 17 00:00:00 2001 From: Dustin Kirkland Date: Thu, 3 Nov 2005 15:41:46 +0000 Subject: [PATCH] Filter rule comparators Currently, audit only supports the "=" and "!=" operators in the -F filter rules. This patch reworks the support for "=" and "!=", and adds support for ">", ">=", "<", and "<=". This turned out to be a pretty clean, and simply process. I ended up using the high order bits of the "field", as suggested by Steve and Amy. This allowed for no changes whatsoever to the netlink communications. See the documentation within the patch in the include/linux/audit.h area, where there is a table that explains the reasoning of the bitmask assignments clearly. The patch adds a new function, audit_comparator(left, op, right). This function will perform the specified comparison (op, which defaults to "==" for backward compatibility) between two values (left and right). If the negate bit is on, it will negate whatever that result was. This value is returned. Signed-off-by: Dustin Kirkland Signed-off-by: David Woodhouse --- include/linux/audit.h | 29 ++++++++++++- kernel/auditsc.c | 117 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 103 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index da3c01955f3d..2408cb77899c 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -98,6 +98,13 @@ #define AUDIT_WORD(nr) ((__u32)((nr)/32)) #define AUDIT_BIT(nr) (1 << ((nr) - AUDIT_WORD(nr)*32)) +/* This bitmask is used to validate user input. It represents all bits that + * are currently used in an audit field constant understood by the kernel. + * If you are adding a new #define AUDIT_, please ensure that + * AUDIT_UNUSED_BITS is updated if need be. */ +#define AUDIT_UNUSED_BITS 0x0FFFFC00 + + /* Rule fields */ /* These are useful when checking the * task structure at task creation time @@ -128,8 +135,28 @@ #define AUDIT_ARG2 (AUDIT_ARG0+2) #define AUDIT_ARG3 (AUDIT_ARG0+3) -#define AUDIT_NEGATE 0x80000000 +#define AUDIT_NEGATE 0x80000000 +/* These are the supported operators. + * 4 2 1 + * = > < + * ------- + * 0 0 0 0 nonsense + * 0 0 1 1 < + * 0 1 0 2 > + * 0 1 1 3 != + * 1 0 0 4 = + * 1 0 1 5 <= + * 1 1 0 6 >= + * 1 1 1 7 all operators + */ +#define AUDIT_LESS_THAN 0x10000000 +#define AUDIT_GREATER_THAN 0x20000000 +#define AUDIT_NOT_EQUAL 0x30000000 +#define AUDIT_EQUAL 0x40000000 +#define AUDIT_LESS_THAN_OR_EQUAL (AUDIT_LESS_THAN|AUDIT_EQUAL) +#define AUDIT_GREATER_THAN_OR_EQUAL (AUDIT_GREATER_THAN|AUDIT_EQUAL) +#define AUDIT_OPERATORS (AUDIT_EQUAL|AUDIT_NOT_EQUAL) /* Status symbols */ /* Mask values */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 51a4f58a4d81..95076fa12202 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2,6 +2,7 @@ * Handles all system-call specific auditing features. * * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. + * Copyright (C) 2005 IBM Corporation * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify @@ -27,6 +28,9 @@ * this file -- see entry.S) is based on a GPL'd patch written by * okir@suse.de and Copyright 2003 SuSE Linux AG. * + * The support of additional filter rules compares (>, <, >=, <=) was + * added by Dustin Kirkland , 2005. + * */ #include @@ -252,6 +256,7 @@ static inline int audit_add_rule(struct audit_rule *rule, struct list_head *list) { struct audit_entry *entry; + int i; /* Do not use the _rcu iterator here, since this is the only * addition routine. */ @@ -261,6 +266,16 @@ static inline int audit_add_rule(struct audit_rule *rule, } } + for (i = 0; i < rule->field_count; i++) { + if (rule->fields[i] & AUDIT_UNUSED_BITS) + return -EINVAL; + if ( rule->fields[i] & AUDIT_NEGATE ) + rule->fields[i] |= AUDIT_NOT_EQUAL; + else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 ) + rule->fields[i] |= AUDIT_EQUAL; + rule->fields[i] &= (~AUDIT_NEGATE); + } + if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) return -ENOMEM; if (audit_copy_rule(&entry->rule, rule)) { @@ -394,6 +409,26 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, return err; } +static int audit_comparator(const u32 left, const u32 op, const u32 right) +{ + switch (op) { + case AUDIT_EQUAL: + return (left == right); + case AUDIT_NOT_EQUAL: + return (left != right); + case AUDIT_LESS_THAN: + return (left < right); + case AUDIT_LESS_THAN_OR_EQUAL: + return (left <= right); + case AUDIT_GREATER_THAN: + return (left > right); + case AUDIT_GREATER_THAN_OR_EQUAL: + return (left >= right); + default: + return -EINVAL; + } +} + /* Compare a task_struct with an audit_rule. Return 1 on match, 0 * otherwise. */ static int audit_filter_rules(struct task_struct *tsk, @@ -404,62 +439,63 @@ static int audit_filter_rules(struct task_struct *tsk, int i, j; for (i = 0; i < rule->field_count; i++) { - u32 field = rule->fields[i] & ~AUDIT_NEGATE; + u32 field = rule->fields[i] & ~AUDIT_OPERATORS; + u32 op = rule->fields[i] & AUDIT_OPERATORS; u32 value = rule->values[i]; int result = 0; switch (field) { case AUDIT_PID: - result = (tsk->pid == value); + result = audit_comparator(tsk->pid, op, value); break; case AUDIT_UID: - result = (tsk->uid == value); + result = audit_comparator(tsk->uid, op, value); break; case AUDIT_EUID: - result = (tsk->euid == value); + result = audit_comparator(tsk->euid, op, value); break; case AUDIT_SUID: - result = (tsk->suid == value); + result = audit_comparator(tsk->suid, op, value); break; case AUDIT_FSUID: - result = (tsk->fsuid == value); + result = audit_comparator(tsk->fsuid, op, value); break; case AUDIT_GID: - result = (tsk->gid == value); + result = audit_comparator(tsk->gid, op, value); break; case AUDIT_EGID: - result = (tsk->egid == value); + result = audit_comparator(tsk->egid, op, value); break; case AUDIT_SGID: - result = (tsk->sgid == value); + result = audit_comparator(tsk->sgid, op, value); break; case AUDIT_FSGID: - result = (tsk->fsgid == value); + result = audit_comparator(tsk->fsgid, op, value); break; case AUDIT_PERS: - result = (tsk->personality == value); + result = audit_comparator(tsk->personality, op, value); break; case AUDIT_ARCH: - if (ctx) - result = (ctx->arch == value); + if (ctx) + result = audit_comparator(ctx->arch, op, value); break; case AUDIT_EXIT: if (ctx && ctx->return_valid) - result = (ctx->return_code == value); + result = audit_comparator(ctx->return_code, op, value); break; case AUDIT_SUCCESS: if (ctx && ctx->return_valid) { if (value) - result = (ctx->return_valid == AUDITSC_SUCCESS); + result = audit_comparator(ctx->return_valid, op, AUDITSC_SUCCESS); else - result = (ctx->return_valid == AUDITSC_FAILURE); + result = audit_comparator(ctx->return_valid, op, AUDITSC_FAILURE); } break; case AUDIT_DEVMAJOR: if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (MAJOR(ctx->names[j].dev)==value) { + if (audit_comparator(MAJOR(ctx->names[j].dev), op, value)) { ++result; break; } @@ -469,7 +505,7 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_DEVMINOR: if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (MINOR(ctx->names[j].dev)==value) { + if (audit_comparator(MINOR(ctx->names[j].dev), op, value)) { ++result; break; } @@ -479,7 +515,7 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_INODE: if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (ctx->names[j].ino == value) { + if (audit_comparator(ctx->names[j].ino, op, value)) { ++result; break; } @@ -489,19 +525,17 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_LOGINUID: result = 0; if (ctx) - result = (ctx->loginuid == value); + result = audit_comparator(ctx->loginuid, op, value); break; case AUDIT_ARG0: case AUDIT_ARG1: case AUDIT_ARG2: case AUDIT_ARG3: if (ctx) - result = (ctx->argv[field-AUDIT_ARG0]==value); + result = audit_comparator(ctx->argv[field-AUDIT_ARG0], op, value); break; } - if (rule->fields[i] & AUDIT_NEGATE) - result = !result; if (!result) return 0; } @@ -550,49 +584,48 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, rcu_read_lock(); if (!list_empty(list)) { - int word = AUDIT_WORD(ctx->major); - int bit = AUDIT_BIT(ctx->major); - - list_for_each_entry_rcu(e, list, list) { - if ((e->rule.mask[word] & bit) == bit - && audit_filter_rules(tsk, &e->rule, ctx, &state)) { - rcu_read_unlock(); - return state; - } - } + int word = AUDIT_WORD(ctx->major); + int bit = AUDIT_BIT(ctx->major); + + list_for_each_entry_rcu(e, list, list) { + if ((e->rule.mask[word] & bit) == bit + && audit_filter_rules(tsk, &e->rule, ctx, &state)) { + rcu_read_unlock(); + return state; + } + } } rcu_read_unlock(); return AUDIT_BUILD_CONTEXT; } static int audit_filter_user_rules(struct netlink_skb_parms *cb, - struct audit_rule *rule, - enum audit_state *state) + struct audit_rule *rule, + enum audit_state *state) { int i; for (i = 0; i < rule->field_count; i++) { - u32 field = rule->fields[i] & ~AUDIT_NEGATE; + u32 field = rule->fields[i] & ~AUDIT_OPERATORS; + u32 op = rule->fields[i] & AUDIT_OPERATORS; u32 value = rule->values[i]; int result = 0; switch (field) { case AUDIT_PID: - result = (cb->creds.pid == value); + result = audit_comparator(cb->creds.pid, op, value); break; case AUDIT_UID: - result = (cb->creds.uid == value); + result = audit_comparator(cb->creds.uid, op, value); break; case AUDIT_GID: - result = (cb->creds.gid == value); + result = audit_comparator(cb->creds.gid, op, value); break; case AUDIT_LOGINUID: - result = (cb->loginuid == value); + result = audit_comparator(cb->loginuid, op, value); break; } - if (rule->fields[i] & AUDIT_NEGATE) - result = !result; if (!result) return 0; } -- cgit v1.2.3 From 90d526c074ae5db484388da56c399acf892b6c17 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Thu, 3 Nov 2005 15:48:08 +0000 Subject: [PATCH] Define new range of userspace messages. The attached patch updates various items for the new user space messages. Please apply. Signed-off-by: Steve Grubb Signed-off-by: David Woodhouse --- include/linux/audit.h | 19 +++++++++++++++---- kernel/audit.c | 2 ++ security/selinux/nlmsgtab.c | 6 ++++-- 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index 2408cb77899c..fd65078e794a 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -33,11 +33,20 @@ * 1200 - 1299 messages internal to the audit daemon * 1300 - 1399 audit event messages * 1400 - 1499 SE Linux use - * 1500 - 1999 future use - * 2000 is for otherwise unclassified kernel audit messages + * 1500 - 1599 kernel LSPP events + * 1600 - 1699 kernel crypto events + * 1700 - 1999 future kernel use (maybe integrity labels and related events) + * 2000 is for otherwise unclassified kernel audit messages (legacy) + * 2001 - 2099 unused (kernel) + * 2100 - 2199 user space anomaly records + * 2200 - 2299 user space actions taken in response to anomalies + * 2300 - 2399 user space generated LSPP events + * 2400 - 2499 user space crypto events + * 2500 - 2999 future user space (maybe integrity labels and related events) * - * Messages from 1000-1199 are bi-directional. 1200-1299 are exclusively user - * space. Anything over that is kernel --> user space communication. + * Messages from 1000-1199 are bi-directional. 1200-1299 & 2100 - 2999 are + * exclusively user space. 1300-2099 is kernel --> user space + * communication. */ #define AUDIT_GET 1000 /* Get status */ #define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */ @@ -54,6 +63,8 @@ #define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */ #define AUDIT_USER_AVC 1107 /* We filter this differently */ #define AUDIT_LAST_USER_MSG 1199 +#define AUDIT_FIRST_USER_MSG2 2100 /* More user space messages */ +#define AUDIT_LAST_USER_MSG2 2999 #define AUDIT_DAEMON_START 1200 /* Daemon startup record */ #define AUDIT_DAEMON_END 1201 /* Daemon normal stop record */ diff --git a/kernel/audit.c b/kernel/audit.c index 973ca5a9e0d6..6d61dd79a605 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -369,6 +369,7 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) break; case AUDIT_USER: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: + case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2: if (!cap_raised(eff_cap, CAP_AUDIT_WRITE)) err = -EPERM; break; @@ -449,6 +450,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) break; case AUDIT_USER: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: + case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2: if (!audit_enabled && msg_type != AUDIT_USER_AVC) return 0; diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 69b9329b2054..d7c0e912c5f3 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -145,8 +145,10 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) break; case SECCLASS_NETLINK_AUDIT_SOCKET: - if (nlmsg_type >= AUDIT_FIRST_USER_MSG && - nlmsg_type <= AUDIT_LAST_USER_MSG) { + if ((nlmsg_type >= AUDIT_FIRST_USER_MSG && + nlmsg_type <= AUDIT_LAST_USER_MSG) || + (nlmsg_type >= AUDIT_FIRST_USER_MSG2 && + nlmsg_type <= AUDIT_LAST_USER_MSG2)) { *perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY; } else { err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms, -- cgit v1.2.3 From f38aa94224c5517a40ba56d453779f70d3229803 Mon Sep 17 00:00:00 2001 From: Amy Griffis Date: Thu, 3 Nov 2005 15:57:06 +0000 Subject: [PATCH] Pass dentry, not just name, in fsnotify creation hooks. The audit hooks (to be added shortly) will want to see dentry->d_inode too, not just the name. Signed-off-by: Amy Griffis Signed-off-by: David Woodhouse --- fs/namei.c | 10 +++++----- include/linux/fsnotify.h | 9 +++++---- kernel/auditsc.c | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index 8dc2b038d5d9..f6619af9e957 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1472,7 +1472,7 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode, DQUOT_INIT(dir); error = dir->i_op->create(dir, dentry, mode, nd); if (!error) - fsnotify_create(dir, dentry->d_name.name); + fsnotify_create(dir, dentry); return error; } @@ -1793,7 +1793,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) DQUOT_INIT(dir); error = dir->i_op->mknod(dir, dentry, mode, dev); if (!error) - fsnotify_create(dir, dentry->d_name.name); + fsnotify_create(dir, dentry); return error; } @@ -1870,7 +1870,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) DQUOT_INIT(dir); error = dir->i_op->mkdir(dir, dentry, mode); if (!error) - fsnotify_mkdir(dir, dentry->d_name.name); + fsnotify_mkdir(dir, dentry); return error; } @@ -2133,7 +2133,7 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i DQUOT_INIT(dir); error = dir->i_op->symlink(dir, dentry, oldname); if (!error) - fsnotify_create(dir, dentry->d_name.name); + fsnotify_create(dir, dentry); return error; } @@ -2210,7 +2210,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de error = dir->i_op->link(old_dentry, dir, new_dentry); mutex_unlock(&old_dentry->d_inode->i_mutex); if (!error) - fsnotify_create(dir, new_dentry->d_name.name); + fsnotify_create(dir, new_dentry); return error; } diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 03b8e7932b83..b5ff64d2f092 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -70,19 +70,20 @@ static inline void fsnotify_inoderemove(struct inode *inode) /* * fsnotify_create - 'name' was linked in */ -static inline void fsnotify_create(struct inode *inode, const char *name) +static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) { inode_dir_notify(inode, DN_CREATE); - inotify_inode_queue_event(inode, IN_CREATE, 0, name); + inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name); } /* * fsnotify_mkdir - directory 'name' was created */ -static inline void fsnotify_mkdir(struct inode *inode, const char *name) +static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) { inode_dir_notify(inode, DN_CREATE); - inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, name); + inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, + dentry->d_name.name); } /* diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 95076fa12202..55ba331757c5 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -515,7 +515,7 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_INODE: if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (audit_comparator(ctx->names[j].ino, op, value)) { + if ( audit_comparator(ctx->names[j].ino, op, value)) { ++result; break; } -- cgit v1.2.3 From 73241ccca0f7786933f1d31b3d86f2456549953a Mon Sep 17 00:00:00 2001 From: Amy Griffis Date: Thu, 3 Nov 2005 16:00:25 +0000 Subject: [PATCH] Collect more inode information during syscall processing. This patch augments the collection of inode info during syscall processing. It represents part of the functionality that was provided by the auditfs patch included in RHEL4. Specifically, it: - Collects information for target inodes created or removed during syscalls. Previous code only collects information for the target inode's parent. - Adds the audit_inode() hook to syscalls that operate on a file descriptor (e.g. fchown), enabling audit to do inode filtering for these calls. - Modifies filtering code to check audit context for either an inode # or a parent inode # matching a given rule. - Modifies logging to provide inode # for both parent and child. - Protect debug info from NULL audit_names.name. [AV: folded a later typo fix from the same author] Signed-off-by: Amy Griffis Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- fs/namei.c | 1 + fs/open.c | 8 ++- fs/xattr.c | 11 +++- include/linux/audit.h | 18 +++++- include/linux/fsnotify.h | 5 ++ kernel/auditsc.c | 142 +++++++++++++++++++++++++++++++++++++++-------- 6 files changed, 157 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index f6619af9e957..51cfc9c3ed00 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1353,6 +1353,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) return -ENOENT; BUG_ON(victim->d_parent->d_inode != dir); + audit_inode_child(victim->d_name.name, victim->d_inode, dir->i_ino); error = permission(dir,MAY_WRITE | MAY_EXEC, NULL); if (error) diff --git a/fs/open.c b/fs/open.c index 70e0230d8e77..70510004d06e 100644 --- a/fs/open.c +++ b/fs/open.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -626,6 +627,8 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) dentry = file->f_dentry; inode = dentry->d_inode; + audit_inode(NULL, inode, 0); + err = -EROFS; if (IS_RDONLY(inode)) goto out_putf; @@ -775,7 +778,10 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group) file = fget(fd); if (file) { - error = chown_common(file->f_dentry, user, group); + struct dentry * dentry; + dentry = file->f_dentry; + audit_inode(NULL, dentry->d_inode, 0); + error = chown_common(dentry, user, group); fput(file); } return error; diff --git a/fs/xattr.c b/fs/xattr.c index 80eca7d3d69f..e416190f5e9c 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -234,12 +235,15 @@ sys_fsetxattr(int fd, char __user *name, void __user *value, size_t size, int flags) { struct file *f; + struct dentry *dentry; int error = -EBADF; f = fget(fd); if (!f) return error; - error = setxattr(f->f_dentry, name, value, size, flags); + dentry = f->f_dentry; + audit_inode(NULL, dentry->d_inode, 0); + error = setxattr(dentry, name, value, size, flags); fput(f); return error; } @@ -458,12 +462,15 @@ asmlinkage long sys_fremovexattr(int fd, char __user *name) { struct file *f; + struct dentry *dentry; int error = -EBADF; f = fget(fd); if (!f) return error; - error = removexattr(f->f_dentry, name); + dentry = f->f_dentry; + audit_inode(NULL, dentry->d_inode, 0); + error = removexattr(dentry, name); fput(f); return error; } diff --git a/include/linux/audit.h b/include/linux/audit.h index fd65078e794a..739b954cb242 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -260,7 +260,20 @@ extern void audit_syscall_entry(struct task_struct *task, int arch, extern void audit_syscall_exit(struct task_struct *task, 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); +extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags); +extern void __audit_inode_child(const char *dname, const struct inode *inode, + unsigned long pino); +static inline void audit_inode(const char *name, const struct inode *inode, + unsigned flags) { + if (unlikely(current->audit_context)) + __audit_inode(name, inode, flags); +} +static inline void audit_inode_child(const char *dname, + const struct inode *inode, + unsigned long pino) { + if (unlikely(current->audit_context)) + __audit_inode_child(dname, inode, pino); +} /* Private API (for audit.c only) */ extern int audit_receive_filter(int type, int pid, int uid, int seq, @@ -283,7 +296,10 @@ extern int audit_filter_user(struct netlink_skb_parms *cb, int type); #define audit_syscall_exit(t,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) +#define __audit_inode_child(d,i,p) do { ; } while (0) #define audit_inode(n,i,f) do { ; } while (0) +#define audit_inode_child(d,i,p) do { ; } while (0) #define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; }) #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0) #define audit_get_loginuid(c) ({ -1; }) diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index b5ff64d2f092..94919c376a72 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -15,6 +15,7 @@ #include #include +#include /* * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir @@ -45,6 +46,8 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, if (source) { inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL); } + audit_inode_child(old_name, source, old_dir->i_ino); + audit_inode_child(new_name, target, new_dir->i_ino); } /* @@ -74,6 +77,7 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) { inode_dir_notify(inode, DN_CREATE); inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name); + audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino); } /* @@ -84,6 +88,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) inode_dir_notify(inode, DN_CREATE); inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, dentry->d_name.name); + audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino); } /* diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 55ba331757c5..73f932b7deb6 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2,6 +2,7 @@ * Handles all system-call specific auditing features. * * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. + * Copyright 2005 Hewlett-Packard Development Company, L.P. * Copyright (C) 2005 IBM Corporation * All Rights Reserved. * @@ -31,11 +32,16 @@ * The support of additional filter rules compares (>, <, >=, <=) was * added by Dustin Kirkland , 2005. * + * Modified by Amy Griffis to collect additional + * filesystem information. */ #include #include #include +#include +#include +#include #include #include #include @@ -97,12 +103,12 @@ enum audit_state { struct audit_names { const char *name; unsigned long ino; + unsigned long pino; dev_t dev; umode_t mode; uid_t uid; gid_t gid; dev_t rdev; - unsigned flags; }; struct audit_aux_data { @@ -515,7 +521,8 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_INODE: if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if ( audit_comparator(ctx->names[j].ino, op, value)) { + if (audit_comparator(ctx->names[j].ino, op, value) || + audit_comparator(ctx->names[j].pino, op, value)) { ++result; break; } @@ -696,17 +703,17 @@ static inline void audit_free_names(struct audit_context *context) #if AUDIT_DEBUG == 2 if (context->auditable ||context->put_count + context->ino_count != context->name_count) { - printk(KERN_ERR "audit.c:%d(:%d): major=%d in_syscall=%d" + printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d" " name_count=%d put_count=%d" " ino_count=%d [NOT freeing]\n", - __LINE__, + __FILE__, __LINE__, context->serial, context->major, context->in_syscall, context->name_count, context->put_count, context->ino_count); for (i = 0; i < context->name_count; i++) printk(KERN_ERR "names[%d] = %p = %s\n", i, context->names[i].name, - context->names[i].name); + context->names[i].name ?: "(null)"); dump_stack(); return; } @@ -932,27 +939,34 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) } } for (i = 0; i < context->name_count; i++) { + unsigned long ino = context->names[i].ino; + unsigned long pino = context->names[i].pino; + ab = audit_log_start(context, gfp_mask, AUDIT_PATH); if (!ab) continue; /* audit_panic has been called */ audit_log_format(ab, "item=%d", i); - if (context->names[i].name) { - audit_log_format(ab, " name="); + + audit_log_format(ab, " name="); + if (context->names[i].name) audit_log_untrustedstring(ab, context->names[i].name); - } - audit_log_format(ab, " flags=%x\n", context->names[i].flags); - - if (context->names[i].ino != (unsigned long)-1) - audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o" - " ouid=%u ogid=%u rdev=%02x:%02x", - context->names[i].ino, - MAJOR(context->names[i].dev), - MINOR(context->names[i].dev), - context->names[i].mode, - context->names[i].uid, - context->names[i].gid, - MAJOR(context->names[i].rdev), + else + audit_log_format(ab, "(null)"); + + if (pino != (unsigned long)-1) + audit_log_format(ab, " parent=%lu", pino); + if (ino != (unsigned long)-1) + audit_log_format(ab, " inode=%lu", ino); + if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1)) + audit_log_format(ab, " dev=%02x:%02x mode=%#o" + " ouid=%u ogid=%u rdev=%02x:%02x", + MAJOR(context->names[i].dev), + MINOR(context->names[i].dev), + context->names[i].mode, + context->names[i].uid, + context->names[i].gid, + MAJOR(context->names[i].rdev), MINOR(context->names[i].rdev)); audit_log_end(ab); } @@ -1174,7 +1188,7 @@ void audit_putname(const char *name) for (i = 0; i < context->name_count; i++) printk(KERN_ERR "name[%d] = %p = %s\n", i, context->names[i].name, - context->names[i].name); + context->names[i].name ?: "(null)"); } #endif __putname(name); @@ -1204,7 +1218,7 @@ void audit_putname(const char *name) * * Called from fs/namei.c:path_lookup(). */ -void audit_inode(const char *name, const struct inode *inode, unsigned flags) +void __audit_inode(const char *name, const struct inode *inode, unsigned flags) { int idx; struct audit_context *context = current->audit_context; @@ -1230,13 +1244,93 @@ void audit_inode(const char *name, const struct inode *inode, unsigned flags) ++context->ino_count; #endif } - context->names[idx].flags = flags; - context->names[idx].ino = inode->i_ino; context->names[idx].dev = inode->i_sb->s_dev; context->names[idx].mode = inode->i_mode; context->names[idx].uid = inode->i_uid; context->names[idx].gid = inode->i_gid; context->names[idx].rdev = inode->i_rdev; + if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && + (strcmp(name, ".") != 0)) { + context->names[idx].ino = (unsigned long)-1; + context->names[idx].pino = inode->i_ino; + } else { + context->names[idx].ino = inode->i_ino; + context->names[idx].pino = (unsigned long)-1; + } +} + +/** + * audit_inode_child - collect inode info for created/removed objects + * @dname: inode's dentry name + * @inode: inode being audited + * @pino: inode number of dentry parent + * + * For syscalls that create or remove filesystem objects, audit_inode + * can only collect information for the filesystem object's parent. + * This call updates the audit context with the child's information. + * Syscalls that create a new filesystem object must be hooked after + * the object is created. Syscalls that remove a filesystem object + * must be hooked prior, in order to capture the target inode during + * unsuccessful attempts. + */ +void __audit_inode_child(const char *dname, const struct inode *inode, + unsigned long pino) +{ + int idx; + struct audit_context *context = current->audit_context; + + if (!context->in_syscall) + return; + + /* determine matching parent */ + if (dname) + for (idx = 0; idx < context->name_count; idx++) + if (context->names[idx].pino == pino) { + const char *n; + const char *name = context->names[idx].name; + int dlen = strlen(dname); + int nlen = name ? strlen(name) : 0; + + if (nlen < dlen) + continue; + + /* disregard trailing slashes */ + n = name + nlen - 1; + while ((*n == '/') && (n > name)) + n--; + + /* find last path component */ + n = n - dlen + 1; + if (n < name) + continue; + else if (n > name) { + if (*--n != '/') + continue; + else + n++; + } + + if (strncmp(n, dname, dlen) == 0) + goto update_context; + } + + /* catch-all in case match not found */ + idx = context->name_count++; + context->names[idx].name = NULL; + context->names[idx].pino = pino; +#if AUDIT_DEBUG + context->ino_count++; +#endif + +update_context: + if (inode) { + context->names[idx].ino = inode->i_ino; + context->names[idx].dev = inode->i_sb->s_dev; + context->names[idx].mode = inode->i_mode; + context->names[idx].uid = inode->i_uid; + context->names[idx].gid = inode->i_gid; + context->names[idx].rdev = inode->i_rdev; + } } /** -- cgit v1.2.3 From c8edc80c8b8c397c53f4f659a05b9ea6208029bf Mon Sep 17 00:00:00 2001 From: Dustin Kirkland Date: Thu, 3 Nov 2005 16:12:36 +0000 Subject: [PATCH] Exclude messages by message type - Add a new, 5th filter called "exclude". - And add a new field AUDIT_MSGTYPE. - Define a new function audit_filter_exclude() that takes a message type as input and examines all rules in the filter. It returns '1' if the message is to be excluded, and '0' otherwise. - Call the audit_filter_exclude() function near the top of audit_log_start() just after asserting audit_initialized. If the message type is not to be audited, return NULL very early, before doing a lot of work. [combined with followup fix for bug in original patch, Nov 4, same author] [combined with later renaming AUDIT_FILTER_EXCLUDE->AUDIT_FILTER_TYPE and audit_filter_exclude() -> audit_filter_type()] Signed-off-by: Dustin Kirkland Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- include/linux/audit.h | 5 ++++- kernel/audit.c | 3 +++ kernel/auditsc.c | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index 739b954cb242..8fa1a8fbc04d 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -92,8 +92,9 @@ #define AUDIT_FILTER_ENTRY 0x02 /* Apply rule at syscall entry */ #define AUDIT_FILTER_WATCH 0x03 /* Apply rule to file system watches */ #define AUDIT_FILTER_EXIT 0x04 /* Apply rule at syscall exit */ +#define AUDIT_FILTER_TYPE 0x05 /* Apply rule at audit_log_start */ -#define AUDIT_NR_FILTERS 5 +#define AUDIT_NR_FILTERS 6 #define AUDIT_FILTER_PREPEND 0x10 /* Prepend to front of list */ @@ -132,6 +133,7 @@ #define AUDIT_LOGINUID 9 #define AUDIT_PERS 10 #define AUDIT_ARCH 11 +#define AUDIT_MSGTYPE 12 /* These are ONLY useful when checking * at syscall exit time (AUDIT_AT_EXIT). */ @@ -289,6 +291,7 @@ extern int audit_sockaddr(int len, void *addr); extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt); extern void audit_signal_info(int sig, struct task_struct *t); extern int audit_filter_user(struct netlink_skb_parms *cb, int type); +extern int audit_filter_type(int type); #else #define audit_alloc(t) ({ 0; }) #define audit_free(t) do { ; } while (0) diff --git a/kernel/audit.c b/kernel/audit.c index 6d61dd79a605..1c3eb1b12bfc 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -702,6 +702,9 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, if (!audit_initialized) return NULL; + if (unlikely(audit_filter_type(type))) + return NULL; + if (gfp_mask & __GFP_WAIT) reserve = 0; else diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 73f932b7deb6..31917ac730af 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -187,7 +187,8 @@ static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { LIST_HEAD_INIT(audit_filter_list[2]), LIST_HEAD_INIT(audit_filter_list[3]), LIST_HEAD_INIT(audit_filter_list[4]), -#if AUDIT_NR_FILTERS != 5 + LIST_HEAD_INIT(audit_filter_list[5]), +#if AUDIT_NR_FILTERS != 6 #error Fix audit_filter_list initialiser #endif }; @@ -663,6 +664,38 @@ int audit_filter_user(struct netlink_skb_parms *cb, int type) return ret; /* Audit by default */ } +int audit_filter_type(int type) +{ + struct audit_entry *e; + int result = 0; + + rcu_read_lock(); + if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE])) + goto unlock_and_return; + + list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], + list) { + struct audit_rule *rule = &e->rule; + int i; + for (i = 0; i < rule->field_count; i++) { + u32 field = rule->fields[i] & ~AUDIT_OPERATORS; + u32 op = rule->fields[i] & AUDIT_OPERATORS; + u32 value = rule->values[i]; + if ( field == AUDIT_MSGTYPE ) { + result = audit_comparator(type, op, value); + if (!result) + break; + } + } + if (result) + goto unlock_and_return; + } +unlock_and_return: + rcu_read_unlock(); + return result; +} + + /* This should be called with task_lock() held. */ static inline struct audit_context *audit_get_context(struct task_struct *tsk, int return_valid, -- cgit v1.2.3 From 8c8570fb8feef2bc166bee75a85748b25cda22d9 Mon Sep 17 00:00:00 2001 From: Dustin Kirkland Date: Thu, 3 Nov 2005 17:15:16 +0000 Subject: [PATCH] Capture selinux subject/object context information. This patch extends existing audit records with subject/object context information. Audit records associated with filesystem inodes, ipc, and tasks now contain SELinux label information in the field "subj" if the item is performing the action, or in "obj" if the item is the receiver of an action. These labels are collected via hooks in SELinux and appended to the appropriate record in the audit code. This additional information is required for Common Criteria Labeled Security Protection Profile (LSPP). [AV: fixed kmalloc flags use] [folded leak fixes] [folded cleanup from akpm (kfree(NULL)] [folded audit_inode_context() leak fix] [folded akpm's fix for audit_ipc_perm() definition in case of !CONFIG_AUDIT] Signed-off-by: Dustin Kirkland Signed-off-by: David Woodhouse Signed-off-by: Andrew Morton Signed-off-by: Al Viro --- include/linux/audit.h | 8 ++- include/linux/security.h | 27 +++++++++ ipc/msg.c | 5 +- ipc/sem.c | 5 +- ipc/shm.c | 4 +- kernel/audit.c | 2 +- kernel/auditsc.c | 142 ++++++++++++++++++++++++++++++++++++++++++++--- security/dummy.c | 6 ++ security/selinux/hooks.c | 96 +++++++++++++++----------------- 9 files changed, 226 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index 8fa1a8fbc04d..1912d8e8ae90 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -285,13 +285,14 @@ 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); +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_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); extern void audit_signal_info(int sig, struct task_struct *t); extern int audit_filter_user(struct netlink_skb_parms *cb, int type); extern int audit_filter_type(int type); +extern int audit_set_macxattr(const char *name); #else #define audit_alloc(t) ({ 0; }) #define audit_free(t) do { ; } while (0) @@ -306,12 +307,13 @@ extern int audit_filter_type(int type); #define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; }) #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) ({ 0; }) +#define audit_ipc_perms(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; }) #define audit_signal_info(s,t) do { ; } while (0) #define audit_filter_user(cb,t) ({ 1; }) +#define audit_set_macxattr(n) do { ; } while (0) #endif #ifdef CONFIG_AUDIT @@ -340,6 +342,7 @@ extern void audit_send_reply(int pid, int seq, int type, int done, int multi, void *payload, int size); extern void audit_log_lost(const char *message); +extern void audit_panic(const char *message); extern struct semaphore audit_netlink_sem; #else #define audit_log(c,g,t,f,...) do { ; } while (0) @@ -350,6 +353,7 @@ extern struct semaphore audit_netlink_sem; #define audit_log_hex(a,b,l) do { ; } while (0) #define audit_log_untrustedstring(a,s) do { ; } while (0) #define audit_log_d_path(b,p,d,v) do { ; } while (0) +#define audit_panic(m) do { ; } while (0) #endif #endif #endif diff --git a/include/linux/security.h b/include/linux/security.h index 7cbef482e13a..ec0bbbc3ffc2 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -869,6 +869,11 @@ 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: @@ -1168,6 +1173,7 @@ struct security_operations { int (*inode_getxattr) (struct dentry *dentry, char *name); int (*inode_listxattr) (struct dentry *dentry); int (*inode_removexattr) (struct dentry *dentry, char *name); + char *(*inode_xattr_getsuffix) (void); int (*inode_getsecurity)(struct inode *inode, const char *name, void *buffer, size_t size, int err); int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags); int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size); @@ -1217,6 +1223,7 @@ 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); @@ -1674,6 +1681,11 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name) return security_ops->inode_removexattr (dentry, name); } +static inline const char *security_inode_xattr_getsuffix(void) +{ + return security_ops->inode_xattr_getsuffix(); +} + static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) { if (unlikely (IS_PRIVATE (inode))) @@ -1869,6 +1881,11 @@ 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); @@ -2316,6 +2333,11 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name) return cap_inode_removexattr(dentry, name); } +static inline const char *security_inode_xattr_getsuffix (void) +{ + return NULL ; +} + static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) { return -EOPNOTSUPP; @@ -2499,6 +2521,11 @@ 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/ipc/msg.c b/ipc/msg.c index fbf757064a32..8c30ec2f6e34 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -429,8 +429,6 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf) return -EFAULT; if (copy_msqid_from_user (&setbuf, buf, version)) return -EFAULT; - if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode))) - return err; break; case IPC_RMID: break; @@ -461,6 +459,9 @@ 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))) + goto out_unlock_up; + err = -EPERM; if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE)) goto out_unlock_up; diff --git a/ipc/sem.c b/ipc/sem.c index 31fd4027d2b5..59696a840be1 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -809,8 +809,6 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun if(cmd == IPC_SET) { if(copy_semid_from_user (&setbuf, arg.buf, version)) return -EFAULT; - if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode))) - return err; } sma = sem_lock(semid); if(sma==NULL) @@ -821,7 +819,6 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun goto out_unlock; } ipcp = &sma->sem_perm; - if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) { err=-EPERM; @@ -838,6 +835,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))) + goto out_unlock; ipcp->uid = setbuf.uid; ipcp->gid = setbuf.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) diff --git a/ipc/shm.c b/ipc/shm.c index 9162123a7b23..a88c8a02e7f3 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -620,13 +620,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) err = -EFAULT; goto out; } - if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode))) - return err; down(&shm_ids.sem); shp = shm_lock(shmid); 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; diff --git a/kernel/audit.c b/kernel/audit.c index 1c3eb1b12bfc..45c123ef77a7 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -142,7 +142,7 @@ static void audit_set_pid(struct audit_buffer *ab, pid_t pid) nlh->nlmsg_pid = pid; } -static void audit_panic(const char *message) +void audit_panic(const char *message) { switch (audit_failure) { diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 31917ac730af..4e2256ec7cf3 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -34,6 +34,9 @@ * * Modified by Amy Griffis to collect additional * filesystem information. + * + * Subject and object context labeling support added by + * and for LSPP certification compliance. */ #include @@ -53,6 +56,7 @@ #include #include #include +#include /* 0 = no checking 1 = put_count checking @@ -109,6 +113,7 @@ struct audit_names { uid_t uid; gid_t gid; dev_t rdev; + char *ctx; }; struct audit_aux_data { @@ -125,6 +130,7 @@ struct audit_aux_data_ipcctl { uid_t uid; gid_t gid; mode_t mode; + char *ctx; }; struct audit_aux_data_socketcall { @@ -743,10 +749,11 @@ static inline void audit_free_names(struct audit_context *context) context->serial, context->major, context->in_syscall, context->name_count, context->put_count, context->ino_count); - for (i = 0; i < context->name_count; i++) + for (i = 0; i < context->name_count; i++) { printk(KERN_ERR "names[%d] = %p = %s\n", i, context->names[i].name, context->names[i].name ?: "(null)"); + } dump_stack(); return; } @@ -756,9 +763,13 @@ static inline void audit_free_names(struct audit_context *context) context->ino_count = 0; #endif - for (i = 0; i < context->name_count; i++) + 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); + } context->name_count = 0; if (context->pwd) dput(context->pwd); @@ -778,6 +789,12 @@ 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); } @@ -862,7 +879,38 @@ static inline void audit_free_context(struct audit_context *context) printk(KERN_ERR "audit: freed %d contexts\n", count); } -static void audit_log_task_info(struct audit_buffer *ab) +static void audit_log_task_context(struct audit_buffer *ab, gfp_t gfp_mask) +{ + char *ctx = NULL; + ssize_t len = 0; + + len = security_getprocattr(current, "current", NULL, 0); + if (len < 0) { + if (len != -EINVAL) + goto error_path; + return; + } + + ctx = kmalloc(len, gfp_mask); + if (!ctx) { + goto error_path; + return; + } + + len = security_getprocattr(current, "current", ctx, len); + if (len < 0 ) + goto error_path; + + audit_log_format(ab, " subj=%s", ctx); + +error_path: + if (ctx) + kfree(ctx); + audit_panic("security_getprocattr error in audit_log_task_context"); + return; +} + +static void audit_log_task_info(struct audit_buffer *ab, gfp_t gfp_mask) { char name[sizeof(current->comm)]; struct mm_struct *mm = current->mm; @@ -875,6 +923,10 @@ static void audit_log_task_info(struct audit_buffer *ab) if (!mm) return; + /* + * this is brittle; all callers that pass GFP_ATOMIC will have + * NULL current->mm and we won't get here. + */ down_read(&mm->mmap_sem); vma = mm->mmap; while (vma) { @@ -888,6 +940,7 @@ static void audit_log_task_info(struct audit_buffer *ab) vma = vma->vm_next; } up_read(&mm->mmap_sem); + audit_log_task_context(ab, gfp_mask); } static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) @@ -923,7 +976,7 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) context->gid, context->euid, context->suid, context->fsuid, context->egid, context->sgid, context->fsgid); - audit_log_task_info(ab); + audit_log_task_info(ab, gfp_mask); audit_log_end(ab); for (aux = context->aux; aux; aux = aux->next) { @@ -936,8 +989,8 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) case AUDIT_IPC: { struct audit_aux_data_ipcctl *axi = (void *)aux; audit_log_format(ab, - " qbytes=%lx iuid=%u igid=%u mode=%x", - axi->qbytes, axi->uid, axi->gid, axi->mode); + " qbytes=%lx iuid=%u igid=%u mode=%x obj=%s", + axi->qbytes, axi->uid, axi->gid, axi->mode, axi->ctx); break; } case AUDIT_SOCKETCALL: { @@ -1001,6 +1054,11 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) 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); + } + audit_log_end(ab); } } @@ -1243,6 +1301,39 @@ void audit_putname(const char *name) #endif } +void audit_inode_context(int idx, const struct inode *inode) +{ + struct audit_context *context = current->audit_context; + char *ctx = NULL; + int len = 0; + + if (!security_inode_xattr_getsuffix()) + return; + + len = security_inode_getsecurity(inode, (char *)security_inode_xattr_getsuffix(), NULL, 0, 0); + if (len < 0) + goto error_path; + + ctx = kmalloc(len, GFP_KERNEL); + if (!ctx) + goto error_path; + + len = security_inode_getsecurity(inode, (char *)security_inode_xattr_getsuffix(), ctx, len, 0); + if (len < 0) + goto error_path; + + kfree(context->names[idx].ctx); + context->names[idx].ctx = ctx; + return; + +error_path: + if (ctx) + kfree(ctx); + audit_panic("error in audit_inode_context"); + return; +} + + /** * audit_inode - store the inode and device from a lookup * @name: name being audited @@ -1282,6 +1373,7 @@ void __audit_inode(const char *name, const struct inode *inode, unsigned flags) context->names[idx].uid = inode->i_uid; context->names[idx].gid = inode->i_gid; context->names[idx].rdev = inode->i_rdev; + audit_inode_context(idx, inode); if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && (strcmp(name, ".") != 0)) { context->names[idx].ino = (unsigned long)-1; @@ -1363,6 +1455,7 @@ update_context: context->names[idx].uid = inode->i_uid; context->names[idx].gid = inode->i_gid; context->names[idx].rdev = inode->i_rdev; + audit_inode_context(idx, inode); } } @@ -1423,6 +1516,38 @@ 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 @@ -1432,7 +1557,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) +int audit_ipc_perms(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; @@ -1440,7 +1565,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) if (likely(!context)) return 0; - ax = kmalloc(sizeof(*ax), GFP_KERNEL); + ax = kmalloc(sizeof(*ax), GFP_ATOMIC); if (!ax) return -ENOMEM; @@ -1448,6 +1573,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) ax->uid = uid; ax->gid = gid; ax->mode = mode; + ax->ctx = audit_ipc_context(ipcp); ax->d.type = AUDIT_IPC; ax->d.next = context->aux; diff --git a/security/dummy.c b/security/dummy.c index f1a5bd98bf10..6febe7d39fa0 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -558,6 +558,11 @@ 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; @@ -959,6 +964,7 @@ 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/hooks.c b/security/selinux/hooks.c index b65c201e9ff5..9c08a19cc81b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -117,6 +117,32 @@ static struct security_operations *secondary_ops = NULL; static LIST_HEAD(superblock_security_head); static DEFINE_SPINLOCK(sb_security_lock); +/* Return security context for a given sid or just the context + length if the buffer is null or length is 0 */ +static int selinux_getsecurity(u32 sid, void *buffer, size_t size) +{ + char *context; + unsigned len; + int rc; + + rc = security_sid_to_context(sid, &context, &len); + if (rc) + return rc; + + if (!buffer || !size) + goto getsecurity_exit; + + if (size < len) { + len = -ERANGE; + goto getsecurity_exit; + } + memcpy(buffer, context, len); + +getsecurity_exit: + kfree(context); + return len; +} + /* Allocate and free functions for each kind of security blob. */ static int task_alloc_security(struct task_struct *task) @@ -2209,6 +2235,11 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) return -EACCES; } +static const char *selinux_inode_xattr_getsuffix(void) +{ + return XATTR_SELINUX_SUFFIX; +} + /* * Copy the in-core inode security context value to the user. If the * getxattr() prior to this succeeded, check to see if we need to @@ -2219,44 +2250,11 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) { struct inode_security_struct *isec = inode->i_security; - char *context; - unsigned len; - int rc; - - if (strcmp(name, XATTR_SELINUX_SUFFIX)) { - rc = -EOPNOTSUPP; - goto out; - } - - rc = security_sid_to_context(isec->sid, &context, &len); - if (rc) - goto out; - - /* Probe for required buffer size */ - if (!buffer || !size) { - rc = len; - goto out_free; - } - if (size < len) { - rc = -ERANGE; - goto out_free; - } + if (strcmp(name, XATTR_SELINUX_SUFFIX)) + return -EOPNOTSUPP; - if (err > 0) { - if ((len == err) && !(memcmp(context, buffer, len))) { - /* Don't need to canonicalize value */ - rc = err; - goto out_free; - } - memset(buffer, 0, size); - } - memcpy(buffer, context, len); - rc = len; -out_free: - kfree(context); -out: - return rc; + return selinux_getsecurity(isec->sid, buffer, size); } static int selinux_inode_setsecurity(struct inode *inode, const char *name, @@ -4022,6 +4020,13 @@ 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) { @@ -4063,8 +4068,7 @@ static int selinux_getprocattr(struct task_struct *p, char *name, void *value, size_t size) { struct task_security_struct *tsec; - u32 sid, len; - char *context; + u32 sid; int error; if (current != p) { @@ -4073,9 +4077,6 @@ static int selinux_getprocattr(struct task_struct *p, return error; } - if (!size) - return -ERANGE; - tsec = p->security; if (!strcmp(name, "current")) @@ -4092,16 +4093,7 @@ static int selinux_getprocattr(struct task_struct *p, if (!sid) return 0; - error = security_sid_to_context(sid, &context, &len); - if (error) - return error; - if (len > size) { - kfree(context); - return -ERANGE; - } - memcpy(value, context, len); - kfree(context); - return len; + return selinux_getsecurity(sid, value, size); } static int selinux_setprocattr(struct task_struct *p, @@ -4259,6 +4251,7 @@ static struct security_operations selinux_ops = { .inode_getxattr = selinux_inode_getxattr, .inode_listxattr = selinux_inode_listxattr, .inode_removexattr = selinux_inode_removexattr, + .inode_xattr_getsuffix = selinux_inode_xattr_getsuffix, .inode_getsecurity = selinux_inode_getsecurity, .inode_setsecurity = selinux_inode_setsecurity, .inode_listsecurity = selinux_inode_listsecurity, @@ -4296,6 +4289,7 @@ 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 7306a0b9b3e2056a616c84841288ca2431a05627 Mon Sep 17 00:00:00 2001 From: Dustin Kirkland Date: Wed, 16 Nov 2005 15:53:13 +0000 Subject: [PATCH] Miscellaneous bug and warning fixes This patch fixes a couple of bugs revealed in new features recently added to -mm1: * fixes warnings due to inconsistent use of const struct inode *inode * fixes bug that prevent a kernel from booting with audit on, and SELinux off due to a missing function in security/dummy.c * fixes a bug that throws spurious audit_panic() messages due to a missing return just before an error_path label * some reasonable house cleaning in audit_ipc_context(), audit_inode_context(), and audit_log_task_context() Signed-off-by: Dustin Kirkland Signed-off-by: David Woodhouse --- include/linux/security.h | 8 ++++---- kernel/auditsc.c | 21 ++++++++++++--------- security/dummy.c | 8 +++++++- security/selinux/hooks.c | 2 +- 4 files changed, 24 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index ec0bbbc3ffc2..2a502250eb5c 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1173,8 +1173,8 @@ struct security_operations { int (*inode_getxattr) (struct dentry *dentry, char *name); int (*inode_listxattr) (struct dentry *dentry); int (*inode_removexattr) (struct dentry *dentry, char *name); - char *(*inode_xattr_getsuffix) (void); - int (*inode_getsecurity)(struct inode *inode, const char *name, void *buffer, size_t size, int err); + const char *(*inode_xattr_getsuffix) (void); + int (*inode_getsecurity)(const struct inode *inode, const char *name, void *buffer, size_t size, int err); int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags); int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size); @@ -1686,7 +1686,7 @@ static inline const char *security_inode_xattr_getsuffix(void) return security_ops->inode_xattr_getsuffix(); } -static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) +static inline int security_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err) { if (unlikely (IS_PRIVATE (inode))) return 0; @@ -2338,7 +2338,7 @@ static inline const char *security_inode_xattr_getsuffix (void) return NULL ; } -static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) +static inline int security_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err) { return -EOPNOTSUPP; } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4e2256ec7cf3..4ef14515da35 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -892,21 +892,20 @@ static void audit_log_task_context(struct audit_buffer *ab, gfp_t gfp_mask) } ctx = kmalloc(len, gfp_mask); - if (!ctx) { + if (!ctx) goto error_path; - return; - } len = security_getprocattr(current, "current", ctx, len); if (len < 0 ) goto error_path; audit_log_format(ab, " subj=%s", ctx); + return; error_path: if (ctx) kfree(ctx); - audit_panic("security_getprocattr error in audit_log_task_context"); + audit_panic("error in audit_log_task_context"); return; } @@ -1304,13 +1303,16 @@ 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 (!security_inode_xattr_getsuffix()) - return; + if (!suffix) + goto ret; - len = security_inode_getsecurity(inode, (char *)security_inode_xattr_getsuffix(), NULL, 0, 0); + len = security_inode_getsecurity(inode, suffix, NULL, 0, 0); + if (len == -EOPNOTSUPP) + goto ret; if (len < 0) goto error_path; @@ -1318,18 +1320,19 @@ void audit_inode_context(int idx, const struct inode *inode) if (!ctx) goto error_path; - len = security_inode_getsecurity(inode, (char *)security_inode_xattr_getsuffix(), ctx, len, 0); + 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; - return; + goto ret; error_path: if (ctx) kfree(ctx); audit_panic("error in audit_inode_context"); +ret: return; } diff --git a/security/dummy.c b/security/dummy.c index 6febe7d39fa0..0a553d39729f 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -378,7 +378,7 @@ static int dummy_inode_removexattr (struct dentry *dentry, char *name) return 0; } -static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) +static int dummy_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err) { return -EOPNOTSUPP; } @@ -393,6 +393,11 @@ static int dummy_inode_listsecurity(struct inode *inode, char *buffer, size_t bu return 0; } +static const char *dummy_inode_xattr_getsuffix(void) +{ + return NULL; +} + static int dummy_file_permission (struct file *file, int mask) { return 0; @@ -930,6 +935,7 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, inode_getxattr); set_to_dummy_if_null(ops, inode_listxattr); set_to_dummy_if_null(ops, inode_removexattr); + set_to_dummy_if_null(ops, inode_xattr_getsuffix); set_to_dummy_if_null(ops, inode_getsecurity); set_to_dummy_if_null(ops, inode_setsecurity); set_to_dummy_if_null(ops, inode_listsecurity); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9c08a19cc81b..81b726b1a419 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2247,7 +2247,7 @@ static const char *selinux_inode_xattr_getsuffix(void) * * Permission check is handled by selinux_inode_getxattr hook. */ -static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) +static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err) { struct inode_security_struct *isec = inode->i_security; -- cgit v1.2.3 From fe7752bab26a9ac0651b695ad4f55659761f68f7 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 15 Dec 2005 18:33:52 +0000 Subject: [PATCH] Fix audit record filtering with !CONFIG_AUDITSYSCALL This fixes the per-user and per-message-type filtering when syscall auditing isn't enabled. [AV: folded followup fix from the same author] Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- include/linux/audit.h | 18 +-- kernel/Makefile | 2 +- kernel/audit.c | 1 + kernel/audit.h | 70 ++++++++++ kernel/auditfilter.c | 378 +++++++++++++++++++++++++++++++++++++++++++++++++ kernel/auditsc.c | 380 +------------------------------------------------- 6 files changed, 459 insertions(+), 390 deletions(-) create mode 100644 kernel/audit.h create mode 100644 kernel/auditfilter.c (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index 1912d8e8ae90..fbc21d6267f3 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -278,8 +278,6 @@ static inline void audit_inode_child(const char *dname, } /* Private API (for audit.c only) */ -extern int audit_receive_filter(int type, int pid, int uid, int seq, - void *data, uid_t loginuid); extern unsigned int audit_serial(void); extern void auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial); @@ -290,8 +288,6 @@ 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); extern void audit_signal_info(int sig, struct task_struct *t); -extern int audit_filter_user(struct netlink_skb_parms *cb, int type); -extern int audit_filter_type(int type); extern int audit_set_macxattr(const char *name); #else #define audit_alloc(t) ({ 0; }) @@ -304,7 +300,6 @@ extern int audit_set_macxattr(const char *name); #define __audit_inode_child(d,i,p) do { ; } while (0) #define audit_inode(n,i,f) do { ; } while (0) #define audit_inode_child(d,i,p) do { ; } while (0) -#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; }) #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; }) @@ -312,7 +307,6 @@ extern int audit_set_macxattr(const char *name); #define audit_sockaddr(len, addr) ({ 0; }) #define audit_avc_path(dentry, mnt) ({ 0; }) #define audit_signal_info(s,t) do { ; } while (0) -#define audit_filter_user(cb,t) ({ 1; }) #define audit_set_macxattr(n) do { ; } while (0) #endif @@ -337,13 +331,11 @@ extern void audit_log_d_path(struct audit_buffer *ab, const char *prefix, struct dentry *dentry, struct vfsmount *vfsmnt); - /* Private API (for auditsc.c only) */ -extern void audit_send_reply(int pid, int seq, int type, - int done, int multi, - void *payload, int size); -extern void audit_log_lost(const char *message); -extern void audit_panic(const char *message); -extern struct semaphore audit_netlink_sem; + /* Private API (for audit.c only) */ +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, uid_t loginuid); #else #define audit_log(c,g,t,f,...) do { ; } while (0) #define audit_log_start(c,g,t) ({ NULL; }) diff --git a/kernel/Makefile b/kernel/Makefile index 4ae0fbde815d..58cf129e5622 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -26,7 +26,7 @@ obj-$(CONFIG_COMPAT) += compat.o obj-$(CONFIG_CPUSETS) += cpuset.o obj-$(CONFIG_IKCONFIG) += configs.o obj-$(CONFIG_STOP_MACHINE) += stop_machine.o -obj-$(CONFIG_AUDIT) += audit.o +obj-$(CONFIG_AUDIT) += audit.o auditfilter.o obj-$(CONFIG_AUDITSYSCALL) += auditsc.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_SYSFS) += ksysfs.o diff --git a/kernel/audit.c b/kernel/audit.c index 45c123ef77a7..07c5d2bdd38c 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -305,6 +305,7 @@ static int kauditd_thread(void *dummy) remove_wait_queue(&kauditd_wait, &wait); } } + return 0; } /** diff --git a/kernel/audit.h b/kernel/audit.h new file mode 100644 index 000000000000..7643e46daeb2 --- /dev/null +++ b/kernel/audit.h @@ -0,0 +1,70 @@ +/* audit -- definition of audit_context structure and supporting types + * + * Copyright 2003-2004 Red Hat, Inc. + * Copyright 2005 Hewlett-Packard Development Company, L.P. + * Copyright 2005 IBM Corporation + * + * 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 + */ + +#include +#include + +/* 0 = no checking + 1 = put_count checking + 2 = verbose put_count checking +*/ +#define AUDIT_DEBUG 0 + +/* At task start time, the audit_state is set in the audit_context using + a per-task filter. At syscall entry, the audit_state is augmented by + the syscall filter. */ +enum audit_state { + AUDIT_DISABLED, /* Do not create per-task audit_context. + * No syscall-specific audit records can + * be generated. */ + AUDIT_SETUP_CONTEXT, /* Create the per-task audit_context, + * but don't necessarily fill it in at + * syscall entry time (i.e., filter + * instead). */ + AUDIT_BUILD_CONTEXT, /* Create the per-task audit_context, + * and always fill it in at syscall + * entry time. This makes a full + * syscall record available if some + * other part of the kernel decides it + * should be recorded. */ + AUDIT_RECORD_CONTEXT /* Create the per-task audit_context, + * always fill it in at syscall entry + * time, and always write out the audit + * record at syscall exit time. */ +}; + +/* Rule lists */ +struct audit_entry { + struct list_head list; + struct rcu_head rcu; + struct audit_rule rule; +}; + + +extern int audit_pid; +extern int audit_comparator(const u32 left, const u32 op, const u32 right); + +extern void audit_send_reply(int pid, int seq, int type, + int done, int multi, + void *payload, int size); +extern void audit_log_lost(const char *message); +extern void audit_panic(const char *message); +extern struct semaphore audit_netlink_sem; diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c new file mode 100644 index 000000000000..7f347c360876 --- /dev/null +++ b/kernel/auditfilter.c @@ -0,0 +1,378 @@ +/* auditfilter.c -- filtering of audit events + * + * Copyright 2003-2004 Red Hat, Inc. + * Copyright 2005 Hewlett-Packard Development Company, L.P. + * Copyright 2005 IBM Corporation + * + * 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 + */ + +#include +#include +#include +#include +#include "audit.h" + +/* There are three lists of rules -- one to search at task creation + * time, one to search at syscall entry time, and another to search at + * syscall exit time. */ +struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { + LIST_HEAD_INIT(audit_filter_list[0]), + LIST_HEAD_INIT(audit_filter_list[1]), + LIST_HEAD_INIT(audit_filter_list[2]), + LIST_HEAD_INIT(audit_filter_list[3]), + LIST_HEAD_INIT(audit_filter_list[4]), + LIST_HEAD_INIT(audit_filter_list[5]), +#if AUDIT_NR_FILTERS != 6 +#error Fix audit_filter_list initialiser +#endif +}; + +/* Copy rule from user-space to kernel-space. Called from + * audit_add_rule during AUDIT_ADD. */ +static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) +{ + int i; + + if (s->action != AUDIT_NEVER + && s->action != AUDIT_POSSIBLE + && s->action != AUDIT_ALWAYS) + return -1; + if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS) + return -1; + if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS) + return -1; + + d->flags = s->flags; + d->action = s->action; + d->field_count = s->field_count; + for (i = 0; i < d->field_count; i++) { + d->fields[i] = s->fields[i]; + d->values[i] = s->values[i]; + } + for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i]; + return 0; +} + +/* Check to see if two rules are identical. It is called from + * audit_add_rule during AUDIT_ADD and + * audit_del_rule during AUDIT_DEL. */ +static inline int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) +{ + int i; + + if (a->flags != b->flags) + return 1; + + if (a->action != b->action) + return 1; + + if (a->field_count != b->field_count) + return 1; + + for (i = 0; i < a->field_count; i++) { + if (a->fields[i] != b->fields[i] + || a->values[i] != b->values[i]) + return 1; + } + + for (i = 0; i < AUDIT_BITMASK_SIZE; i++) + if (a->mask[i] != b->mask[i]) + return 1; + + return 0; +} + +/* Note that audit_add_rule and audit_del_rule are called via + * audit_receive() in audit.c, and are protected by + * audit_netlink_sem. */ +static inline int audit_add_rule(struct audit_rule *rule, + struct list_head *list) +{ + struct audit_entry *entry; + int i; + + /* Do not use the _rcu iterator here, since this is the only + * addition routine. */ + list_for_each_entry(entry, list, list) { + if (!audit_compare_rule(rule, &entry->rule)) { + return -EEXIST; + } + } + + for (i = 0; i < rule->field_count; i++) { + if (rule->fields[i] & AUDIT_UNUSED_BITS) + return -EINVAL; + if ( rule->fields[i] & AUDIT_NEGATE ) + rule->fields[i] |= AUDIT_NOT_EQUAL; + else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 ) + rule->fields[i] |= AUDIT_EQUAL; + rule->fields[i] &= (~AUDIT_NEGATE); + } + + if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) + return -ENOMEM; + if (audit_copy_rule(&entry->rule, rule)) { + kfree(entry); + return -EINVAL; + } + + if (entry->rule.flags & AUDIT_FILTER_PREPEND) { + entry->rule.flags &= ~AUDIT_FILTER_PREPEND; + list_add_rcu(&entry->list, list); + } else { + list_add_tail_rcu(&entry->list, list); + } + + return 0; +} + +static inline void audit_free_rule(struct rcu_head *head) +{ + struct audit_entry *e = container_of(head, struct audit_entry, rcu); + kfree(e); +} + +/* Note that audit_add_rule and audit_del_rule are called via + * audit_receive() in audit.c, and are protected by + * audit_netlink_sem. */ +static inline int audit_del_rule(struct audit_rule *rule, + struct list_head *list) +{ + struct audit_entry *e; + + /* Do not use the _rcu iterator here, since this is the only + * deletion routine. */ + list_for_each_entry(e, list, list) { + if (!audit_compare_rule(rule, &e->rule)) { + list_del_rcu(&e->list); + call_rcu(&e->rcu, audit_free_rule); + return 0; + } + } + return -ENOENT; /* No matching rule */ +} + +static int audit_list_rules(void *_dest) +{ + int pid, seq; + int *dest = _dest; + struct audit_entry *entry; + int i; + + pid = dest[0]; + seq = dest[1]; + kfree(dest); + + down(&audit_netlink_sem); + + /* The *_rcu iterators not needed here because we are + always called with audit_netlink_sem held. */ + for (i=0; irule, sizeof(entry->rule)); + } + audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); + + up(&audit_netlink_sem); + return 0; +} + +/** + * audit_receive_filter - apply all rules to the specified message type + * @type: audit message type + * @pid: target pid for netlink audit messages + * @uid: target uid for netlink audit messages + * @seq: netlink audit message sequence (serial) number + * @data: payload data + * @loginuid: loginuid of sender + */ +int audit_receive_filter(int type, int pid, int uid, int seq, void *data, + uid_t loginuid) +{ + struct task_struct *tsk; + int *dest; + int err = 0; + unsigned listnr; + + switch (type) { + case AUDIT_LIST: + /* We can't just spew out the rules here because we might fill + * the available socket buffer space and deadlock waiting for + * auditctl to read from it... which isn't ever going to + * happen if we're actually running in the context of auditctl + * trying to _send_ the stuff */ + + dest = kmalloc(2 * sizeof(int), GFP_KERNEL); + if (!dest) + return -ENOMEM; + dest[0] = pid; + dest[1] = seq; + + tsk = kthread_run(audit_list_rules, dest, "audit_list_rules"); + if (IS_ERR(tsk)) { + kfree(dest); + err = PTR_ERR(tsk); + } + break; + case AUDIT_ADD: + listnr = ((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; + switch(listnr) { + default: + return -EINVAL; + + case AUDIT_FILTER_USER: + case AUDIT_FILTER_TYPE: +#ifdef CONFIG_AUDITSYSCALL + case AUDIT_FILTER_ENTRY: + case AUDIT_FILTER_EXIT: + case AUDIT_FILTER_TASK: +#endif + ; + } + err = audit_add_rule(data, &audit_filter_list[listnr]); + if (!err) + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u added an audit rule\n", loginuid); + break; + case AUDIT_DEL: + listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; + if (listnr >= AUDIT_NR_FILTERS) + return -EINVAL; + + err = audit_del_rule(data, &audit_filter_list[listnr]); + if (!err) + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u removed an audit rule\n", loginuid); + break; + default: + return -EINVAL; + } + + return err; +} + +int audit_comparator(const u32 left, const u32 op, const u32 right) +{ + switch (op) { + case AUDIT_EQUAL: + return (left == right); + case AUDIT_NOT_EQUAL: + return (left != right); + case AUDIT_LESS_THAN: + return (left < right); + case AUDIT_LESS_THAN_OR_EQUAL: + return (left <= right); + case AUDIT_GREATER_THAN: + return (left > right); + case AUDIT_GREATER_THAN_OR_EQUAL: + return (left >= right); + default: + return -EINVAL; + } +} + + + +static int audit_filter_user_rules(struct netlink_skb_parms *cb, + struct audit_rule *rule, + enum audit_state *state) +{ + int i; + + for (i = 0; i < rule->field_count; i++) { + u32 field = rule->fields[i] & ~AUDIT_OPERATORS; + u32 op = rule->fields[i] & AUDIT_OPERATORS; + u32 value = rule->values[i]; + int result = 0; + + switch (field) { + case AUDIT_PID: + result = audit_comparator(cb->creds.pid, op, value); + break; + case AUDIT_UID: + result = audit_comparator(cb->creds.uid, op, value); + break; + case AUDIT_GID: + result = audit_comparator(cb->creds.gid, op, value); + break; + case AUDIT_LOGINUID: + result = audit_comparator(cb->loginuid, op, value); + break; + } + + if (!result) + return 0; + } + switch (rule->action) { + case AUDIT_NEVER: *state = AUDIT_DISABLED; break; + case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT; break; + case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; + } + return 1; +} + +int audit_filter_user(struct netlink_skb_parms *cb, int type) +{ + struct audit_entry *e; + enum audit_state state; + int ret = 1; + + rcu_read_lock(); + list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) { + if (audit_filter_user_rules(cb, &e->rule, &state)) { + if (state == AUDIT_DISABLED) + ret = 0; + break; + } + } + rcu_read_unlock(); + + return ret; /* Audit by default */ +} + +int audit_filter_type(int type) +{ + struct audit_entry *e; + int result = 0; + + rcu_read_lock(); + if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE])) + goto unlock_and_return; + + list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], + list) { + struct audit_rule *rule = &e->rule; + int i; + for (i = 0; i < rule->field_count; i++) { + u32 field = rule->fields[i] & ~AUDIT_OPERATORS; + u32 op = rule->fields[i] & AUDIT_OPERATORS; + u32 value = rule->values[i]; + if ( field == AUDIT_MSGTYPE ) { + result = audit_comparator(type, op, value); + if (!result) + break; + } + } + if (result) + goto unlock_and_return; + } +unlock_and_return: + rcu_read_unlock(); + return result; +} + + diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4ef14515da35..17719b303638 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -52,17 +52,15 @@ #include #include #include -#include #include #include #include #include +#include -/* 0 = no checking - 1 = put_count checking - 2 = verbose put_count checking -*/ -#define AUDIT_DEBUG 0 +#include "audit.h" + +extern struct list_head audit_filter_list[]; /* No syscall auditing will take place unless audit_enabled != 0. */ extern int audit_enabled; @@ -76,29 +74,6 @@ extern int audit_enabled; * path_lookup. */ #define AUDIT_NAMES_RESERVED 7 -/* At task start time, the audit_state is set in the audit_context using - a per-task filter. At syscall entry, the audit_state is augmented by - the syscall filter. */ -enum audit_state { - AUDIT_DISABLED, /* Do not create per-task audit_context. - * No syscall-specific audit records can - * be generated. */ - AUDIT_SETUP_CONTEXT, /* Create the per-task audit_context, - * but don't necessarily fill it in at - * syscall entry time (i.e., filter - * instead). */ - AUDIT_BUILD_CONTEXT, /* Create the per-task audit_context, - * and always fill it in at syscall - * entry time. This makes a full - * syscall record available if some - * other part of the kernel decides it - * should be recorded. */ - AUDIT_RECORD_CONTEXT /* Create the per-task audit_context, - * always fill it in at syscall entry - * time, and always write out the audit - * record at syscall exit time. */ -}; - /* When fs/namei.c:getname() is called, we store the pointer in name and * we don't let putname() free it (instead we free all of the saved * pointers at syscall exit time). @@ -183,264 +158,6 @@ struct audit_context { #endif }; - /* Public API */ -/* There are three lists of rules -- one to search at task creation - * time, one to search at syscall entry time, and another to search at - * syscall exit time. */ -static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { - LIST_HEAD_INIT(audit_filter_list[0]), - LIST_HEAD_INIT(audit_filter_list[1]), - LIST_HEAD_INIT(audit_filter_list[2]), - LIST_HEAD_INIT(audit_filter_list[3]), - LIST_HEAD_INIT(audit_filter_list[4]), - LIST_HEAD_INIT(audit_filter_list[5]), -#if AUDIT_NR_FILTERS != 6 -#error Fix audit_filter_list initialiser -#endif -}; - -struct audit_entry { - struct list_head list; - struct rcu_head rcu; - struct audit_rule rule; -}; - -extern int audit_pid; - -/* Copy rule from user-space to kernel-space. Called from - * audit_add_rule during AUDIT_ADD. */ -static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) -{ - int i; - - if (s->action != AUDIT_NEVER - && s->action != AUDIT_POSSIBLE - && s->action != AUDIT_ALWAYS) - return -1; - if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS) - return -1; - if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS) - return -1; - - d->flags = s->flags; - d->action = s->action; - d->field_count = s->field_count; - for (i = 0; i < d->field_count; i++) { - d->fields[i] = s->fields[i]; - d->values[i] = s->values[i]; - } - for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i]; - return 0; -} - -/* Check to see if two rules are identical. It is called from - * audit_add_rule during AUDIT_ADD and - * audit_del_rule during AUDIT_DEL. */ -static inline int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) -{ - int i; - - if (a->flags != b->flags) - return 1; - - if (a->action != b->action) - return 1; - - if (a->field_count != b->field_count) - return 1; - - for (i = 0; i < a->field_count; i++) { - if (a->fields[i] != b->fields[i] - || a->values[i] != b->values[i]) - return 1; - } - - for (i = 0; i < AUDIT_BITMASK_SIZE; i++) - if (a->mask[i] != b->mask[i]) - return 1; - - return 0; -} - -/* Note that audit_add_rule and audit_del_rule are called via - * audit_receive() in audit.c, and are protected by - * audit_netlink_sem. */ -static inline int audit_add_rule(struct audit_rule *rule, - struct list_head *list) -{ - struct audit_entry *entry; - int i; - - /* Do not use the _rcu iterator here, since this is the only - * addition routine. */ - list_for_each_entry(entry, list, list) { - if (!audit_compare_rule(rule, &entry->rule)) { - return -EEXIST; - } - } - - for (i = 0; i < rule->field_count; i++) { - if (rule->fields[i] & AUDIT_UNUSED_BITS) - return -EINVAL; - if ( rule->fields[i] & AUDIT_NEGATE ) - rule->fields[i] |= AUDIT_NOT_EQUAL; - else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 ) - rule->fields[i] |= AUDIT_EQUAL; - rule->fields[i] &= (~AUDIT_NEGATE); - } - - if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) - return -ENOMEM; - if (audit_copy_rule(&entry->rule, rule)) { - kfree(entry); - return -EINVAL; - } - - if (entry->rule.flags & AUDIT_FILTER_PREPEND) { - entry->rule.flags &= ~AUDIT_FILTER_PREPEND; - list_add_rcu(&entry->list, list); - } else { - list_add_tail_rcu(&entry->list, list); - } - - return 0; -} - -static inline void audit_free_rule(struct rcu_head *head) -{ - struct audit_entry *e = container_of(head, struct audit_entry, rcu); - kfree(e); -} - -/* Note that audit_add_rule and audit_del_rule are called via - * audit_receive() in audit.c, and are protected by - * audit_netlink_sem. */ -static inline int audit_del_rule(struct audit_rule *rule, - struct list_head *list) -{ - struct audit_entry *e; - - /* Do not use the _rcu iterator here, since this is the only - * deletion routine. */ - list_for_each_entry(e, list, list) { - if (!audit_compare_rule(rule, &e->rule)) { - list_del_rcu(&e->list); - call_rcu(&e->rcu, audit_free_rule); - return 0; - } - } - return -ENOENT; /* No matching rule */ -} - -static int audit_list_rules(void *_dest) -{ - int pid, seq; - int *dest = _dest; - struct audit_entry *entry; - int i; - - pid = dest[0]; - seq = dest[1]; - kfree(dest); - - down(&audit_netlink_sem); - - /* The *_rcu iterators not needed here because we are - always called with audit_netlink_sem held. */ - for (i=0; irule, sizeof(entry->rule)); - } - audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); - - up(&audit_netlink_sem); - return 0; -} - -/** - * audit_receive_filter - apply all rules to the specified message type - * @type: audit message type - * @pid: target pid for netlink audit messages - * @uid: target uid for netlink audit messages - * @seq: netlink audit message sequence (serial) number - * @data: payload data - * @loginuid: loginuid of sender - */ -int audit_receive_filter(int type, int pid, int uid, int seq, void *data, - uid_t loginuid) -{ - struct task_struct *tsk; - int *dest; - int err = 0; - unsigned listnr; - - switch (type) { - case AUDIT_LIST: - /* We can't just spew out the rules here because we might fill - * the available socket buffer space and deadlock waiting for - * auditctl to read from it... which isn't ever going to - * happen if we're actually running in the context of auditctl - * trying to _send_ the stuff */ - - dest = kmalloc(2 * sizeof(int), GFP_KERNEL); - if (!dest) - return -ENOMEM; - dest[0] = pid; - dest[1] = seq; - - tsk = kthread_run(audit_list_rules, dest, "audit_list_rules"); - if (IS_ERR(tsk)) { - kfree(dest); - err = PTR_ERR(tsk); - } - break; - case AUDIT_ADD: - listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; - if (listnr >= AUDIT_NR_FILTERS) - return -EINVAL; - - err = audit_add_rule(data, &audit_filter_list[listnr]); - if (!err) - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u added an audit rule\n", loginuid); - break; - case AUDIT_DEL: - listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; - if (listnr >= AUDIT_NR_FILTERS) - return -EINVAL; - - err = audit_del_rule(data, &audit_filter_list[listnr]); - if (!err) - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u removed an audit rule\n", loginuid); - break; - default: - return -EINVAL; - } - - return err; -} - -static int audit_comparator(const u32 left, const u32 op, const u32 right) -{ - switch (op) { - case AUDIT_EQUAL: - return (left == right); - case AUDIT_NOT_EQUAL: - return (left != right); - case AUDIT_LESS_THAN: - return (left < right); - case AUDIT_LESS_THAN_OR_EQUAL: - return (left <= right); - case AUDIT_GREATER_THAN: - return (left > right); - case AUDIT_GREATER_THAN_OR_EQUAL: - return (left >= right); - default: - return -EINVAL; - } -} /* Compare a task_struct with an audit_rule. Return 1 on match, 0 * otherwise. */ @@ -613,95 +330,6 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, return AUDIT_BUILD_CONTEXT; } -static int audit_filter_user_rules(struct netlink_skb_parms *cb, - struct audit_rule *rule, - enum audit_state *state) -{ - int i; - - for (i = 0; i < rule->field_count; i++) { - u32 field = rule->fields[i] & ~AUDIT_OPERATORS; - u32 op = rule->fields[i] & AUDIT_OPERATORS; - u32 value = rule->values[i]; - int result = 0; - - switch (field) { - case AUDIT_PID: - result = audit_comparator(cb->creds.pid, op, value); - break; - case AUDIT_UID: - result = audit_comparator(cb->creds.uid, op, value); - break; - case AUDIT_GID: - result = audit_comparator(cb->creds.gid, op, value); - break; - case AUDIT_LOGINUID: - result = audit_comparator(cb->loginuid, op, value); - break; - } - - if (!result) - return 0; - } - switch (rule->action) { - case AUDIT_NEVER: *state = AUDIT_DISABLED; break; - case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT; break; - case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; - } - return 1; -} - -int audit_filter_user(struct netlink_skb_parms *cb, int type) -{ - struct audit_entry *e; - enum audit_state state; - int ret = 1; - - rcu_read_lock(); - list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) { - if (audit_filter_user_rules(cb, &e->rule, &state)) { - if (state == AUDIT_DISABLED) - ret = 0; - break; - } - } - rcu_read_unlock(); - - return ret; /* Audit by default */ -} - -int audit_filter_type(int type) -{ - struct audit_entry *e; - int result = 0; - - rcu_read_lock(); - if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE])) - goto unlock_and_return; - - list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], - list) { - struct audit_rule *rule = &e->rule; - int i; - for (i = 0; i < rule->field_count; i++) { - u32 field = rule->fields[i] & ~AUDIT_OPERATORS; - u32 op = rule->fields[i] & AUDIT_OPERATORS; - u32 value = rule->values[i]; - if ( field == AUDIT_MSGTYPE ) { - result = audit_comparator(type, op, value); - if (!result) - break; - } - } - if (result) - goto unlock_and_return; - } -unlock_and_return: - rcu_read_unlock(); - return result; -} - - /* This should be called with task_lock() held. */ static inline struct audit_context *audit_get_context(struct task_struct *tsk, int return_valid, -- cgit v1.2.3 From af601e4623d0303bfafa54ec728b7ae8493a8e1b Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Wed, 4 Jan 2006 14:08:39 +0000 Subject: [PATCH] SE Linux audit events Attached is a patch that hardwires important SE Linux events to the audit system. Please Apply. Signed-off-by: Steve Grubb Acked-by: Stephen Smalley Signed-off-by: David Woodhouse --- include/linux/audit.h | 3 +++ security/selinux/selinuxfs.c | 11 +++++++++++ security/selinux/ss/services.c | 15 +++++++++------ 3 files changed, 23 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index fbc21d6267f3..8868c96ca8a2 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -83,6 +83,9 @@ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ #define AUDIT_AVC_PATH 1402 /* dentry, vfsmount pair from avc */ +#define AUDIT_MAC_POLICY_LOAD 1403 /* Policy file load */ +#define AUDIT_MAC_STATUS 1404 /* Changed enforcing,permissive,off */ +#define AUDIT_MAC_CONFIG_CHANGE 1405 /* Changes to booleans */ #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index b5fa02d17b1e..5eba6664eac0 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -126,6 +127,10 @@ static ssize_t sel_write_enforce(struct file * file, const char __user * buf, length = task_has_security(current, SECURITY__SETENFORCE); if (length) goto out; + audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, + "enforcing=%d old_enforcing=%d auid=%u", new_value, + selinux_enforcing, + audit_get_loginuid(current->audit_context)); selinux_enforcing = new_value; if (selinux_enforcing) avc_ss_reset(0); @@ -176,6 +181,9 @@ static ssize_t sel_write_disable(struct file * file, const char __user * buf, length = selinux_disable(); if (length < 0) goto out; + audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, + "selinux=0 auid=%u", + audit_get_loginuid(current->audit_context)); } length = count; @@ -261,6 +269,9 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, length = ret; else length = count; + audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, + "policy loaded auid=%u", + audit_get_loginuid(current->audit_context)); out: up(&sel_sem); vfree(data); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 8a764928ff4b..d877cd16a813 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1758,19 +1758,22 @@ int security_set_bools(int len, int *values) goto out; } - printk(KERN_INFO "security: committed booleans { "); for (i = 0; i < len; i++) { + if (!!values[i] != policydb.bool_val_to_struct[i]->state) { + audit_log(current->audit_context, GFP_ATOMIC, + AUDIT_MAC_CONFIG_CHANGE, + "bool=%s val=%d old_val=%d auid=%u", + policydb.p_bool_val_to_name[i], + !!values[i], + policydb.bool_val_to_struct[i]->state, + audit_get_loginuid(current->audit_context)); + } if (values[i]) { policydb.bool_val_to_struct[i]->state = 1; } else { policydb.bool_val_to_struct[i]->state = 0; } - if (i != 0) - printk(", "); - printk("%s:%d", policydb.p_bool_val_to_name[i], - policydb.bool_val_to_struct[i]->state); } - printk(" }\n"); for (cur = policydb.cond_list; cur != NULL; cur = cur->next) { rc = evaluate_cond_node(&policydb, cur); -- cgit v1.2.3 From 93315ed6dd12dacfc941f9eb8ca0293aadf99793 Mon Sep 17 00:00:00 2001 From: Amy Griffis Date: Tue, 7 Feb 2006 12:05:27 -0500 Subject: [PATCH] audit string fields interface + consumer Updated patch to dynamically allocate audit rule fields in kernel's internal representation. Added unlikely() calls for testing memory allocation result. Amy Griffis wrote: [Wed Jan 11 2006, 02:02:31PM EST] > Modify audit's kernel-userspace interface to allow the specification > of string fields in audit rules. > > Signed-off-by: Amy Griffis Signed-off-by: Al Viro (cherry picked from 5ffc4a863f92351b720fe3e9c5cd647accff9e03 commit) --- include/linux/audit.h | 31 ++- kernel/audit.c | 19 +- kernel/audit.h | 23 ++- kernel/auditfilter.c | 467 +++++++++++++++++++++++++++++++++----------- kernel/auditsc.c | 50 +++-- security/selinux/nlmsgtab.c | 3 + 6 files changed, 448 insertions(+), 145 deletions(-) (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index 8868c96ca8a2..8a3b98175c25 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -50,15 +50,18 @@ */ #define AUDIT_GET 1000 /* Get status */ #define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */ -#define AUDIT_LIST 1002 /* List syscall filtering rules */ -#define AUDIT_ADD 1003 /* Add syscall filtering rule */ -#define AUDIT_DEL 1004 /* Delete syscall filtering rule */ +#define AUDIT_LIST 1002 /* List syscall rules -- deprecated */ +#define AUDIT_ADD 1003 /* Add syscall rule -- deprecated */ +#define AUDIT_DEL 1004 /* Delete syscall rule -- deprecated */ #define AUDIT_USER 1005 /* Message from userspace -- deprecated */ #define AUDIT_LOGIN 1006 /* Define the login id and information */ #define AUDIT_WATCH_INS 1007 /* Insert file/dir watch entry */ #define AUDIT_WATCH_REM 1008 /* Remove file/dir watch entry */ #define AUDIT_WATCH_LIST 1009 /* List all file/dir watches */ #define AUDIT_SIGNAL_INFO 1010 /* Get info about sender of signal to auditd */ +#define AUDIT_ADD_RULE 1011 /* Add syscall filtering rule */ +#define AUDIT_DEL_RULE 1012 /* Delete syscall filtering rule */ +#define AUDIT_LIST_RULES 1013 /* List syscall filtering rules */ #define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */ #define AUDIT_USER_AVC 1107 /* We filter this differently */ @@ -229,6 +232,26 @@ struct audit_status { __u32 backlog; /* messages waiting in queue */ }; +/* audit_rule_data supports filter rules with both integer and string + * fields. It corresponds with AUDIT_ADD_RULE, AUDIT_DEL_RULE and + * AUDIT_LIST_RULES requests. + */ +struct audit_rule_data { + __u32 flags; /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */ + __u32 action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */ + __u32 field_count; + __u32 mask[AUDIT_BITMASK_SIZE]; + __u32 fields[AUDIT_MAX_FIELDS]; + __u32 values[AUDIT_MAX_FIELDS]; + __u32 fieldflags[AUDIT_MAX_FIELDS]; + __u32 buflen; /* total length of string fields */ + char buf[0]; /* string fields buffer */ +}; + +/* audit_rule is supported to maintain backward compatibility with + * userspace. It supports integer fields only and corresponds to + * AUDIT_ADD, AUDIT_DEL and AUDIT_LIST requests. + */ struct audit_rule { /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */ __u32 flags; /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */ __u32 action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */ @@ -338,7 +361,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, uid_t loginuid); + void *data, size_t datasz, uid_t loginuid); #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 07c5d2bdd38c..4eb97b62d7fa 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -52,6 +52,7 @@ #include #include +#include #include #include @@ -361,9 +362,12 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) switch (msg_type) { case AUDIT_GET: case AUDIT_LIST: + case AUDIT_LIST_RULES: case AUDIT_SET: case AUDIT_ADD: + case AUDIT_ADD_RULE: case AUDIT_DEL: + case AUDIT_DEL_RULE: case AUDIT_SIGNAL_INFO: if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) err = -EPERM; @@ -470,12 +474,23 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) break; case AUDIT_ADD: case AUDIT_DEL: - if (nlh->nlmsg_len < sizeof(struct audit_rule)) + if (nlmsg_len(nlh) < sizeof(struct audit_rule)) return -EINVAL; /* fallthrough */ case AUDIT_LIST: err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, - uid, seq, data, loginuid); + uid, seq, data, nlmsg_len(nlh), + loginuid); + break; + case AUDIT_ADD_RULE: + case AUDIT_DEL_RULE: + if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) + return -EINVAL; + /* fallthrough */ + case AUDIT_LIST_RULES: + err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, + uid, seq, data, nlmsg_len(nlh), + loginuid); break; case AUDIT_SIGNAL_INFO: sig_data.uid = audit_sig_uid; diff --git a/kernel/audit.h b/kernel/audit.h index 7643e46daeb2..4b602cdcabef 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -52,10 +52,27 @@ enum audit_state { }; /* Rule lists */ +struct audit_field { + u32 type; + u32 val; + u32 op; +}; + +struct audit_krule { + int vers_ops; + u32 flags; + u32 listnr; + u32 action; + u32 mask[AUDIT_BITMASK_SIZE]; + u32 buflen; /* for data alloc on list rules */ + u32 field_count; + struct audit_field *fields; +}; + struct audit_entry { - struct list_head list; - struct rcu_head rcu; - struct audit_rule rule; + struct list_head list; + struct rcu_head rcu; + struct audit_krule rule; }; diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index a3a32752f973..686d514a3518 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -40,52 +40,279 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { #endif }; -/* Copy rule from user-space to kernel-space. Called from - * audit_add_rule during AUDIT_ADD. */ -static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) +static inline void audit_free_rule(struct audit_entry *e) { + kfree(e->rule.fields); + kfree(e); +} + +static inline void audit_free_rule_rcu(struct rcu_head *head) +{ + struct audit_entry *e = container_of(head, struct audit_entry, rcu); + audit_free_rule(e); +} + +/* Unpack a filter field's string representation from user-space + * buffer. */ +static __attribute__((unused)) char *audit_unpack_string(void **bufp, size_t *remain, size_t len) +{ + char *str; + + if (!*bufp || (len == 0) || (len > *remain)) + return ERR_PTR(-EINVAL); + + /* Of the currently implemented string fields, PATH_MAX + * defines the longest valid length. + */ + if (len > PATH_MAX) + return ERR_PTR(-ENAMETOOLONG); + + str = kmalloc(len + 1, GFP_KERNEL); + if (unlikely(!str)) + return ERR_PTR(-ENOMEM); + + memcpy(str, *bufp, len); + str[len] = 0; + *bufp += len; + *remain -= len; + + return str; +} + +/* Common user-space to kernel rule translation. */ +static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) +{ + unsigned listnr; + struct audit_entry *entry; + struct audit_field *fields; + int i, err; + + err = -EINVAL; + listnr = rule->flags & ~AUDIT_FILTER_PREPEND; + switch(listnr) { + default: + goto exit_err; + case AUDIT_FILTER_USER: + case AUDIT_FILTER_TYPE: +#ifdef CONFIG_AUDITSYSCALL + case AUDIT_FILTER_ENTRY: + case AUDIT_FILTER_EXIT: + case AUDIT_FILTER_TASK: +#endif + ; + } + if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE && + rule->action != AUDIT_ALWAYS) + goto exit_err; + if (rule->field_count > AUDIT_MAX_FIELDS) + goto exit_err; + + err = -ENOMEM; + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (unlikely(!entry)) + goto exit_err; + fields = kmalloc(sizeof(*fields) * rule->field_count, GFP_KERNEL); + if (unlikely(!fields)) { + kfree(entry); + goto exit_err; + } + + memset(&entry->rule, 0, sizeof(struct audit_krule)); + memset(fields, 0, sizeof(struct audit_field)); + + entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND; + entry->rule.listnr = listnr; + entry->rule.action = rule->action; + entry->rule.field_count = rule->field_count; + entry->rule.fields = fields; + + for (i = 0; i < AUDIT_BITMASK_SIZE; i++) + entry->rule.mask[i] = rule->mask[i]; + + return entry; + +exit_err: + return ERR_PTR(err); +} + +/* Translate struct audit_rule to kernel's rule respresentation. + * Exists for backward compatibility with userspace. */ +static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) +{ + struct audit_entry *entry; + int err = 0; int i; - if (s->action != AUDIT_NEVER - && s->action != AUDIT_POSSIBLE - && s->action != AUDIT_ALWAYS) - return -1; - if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS) - return -1; - if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS) - return -1; - - d->flags = s->flags; - d->action = s->action; - d->field_count = s->field_count; - for (i = 0; i < d->field_count; i++) { - d->fields[i] = s->fields[i]; - d->values[i] = s->values[i]; + entry = audit_to_entry_common(rule); + if (IS_ERR(entry)) + goto exit_nofree; + + for (i = 0; i < rule->field_count; i++) { + struct audit_field *f = &entry->rule.fields[i]; + + if (rule->fields[i] & AUDIT_UNUSED_BITS) { + err = -EINVAL; + goto exit_free; + } + + f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); + f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); + f->val = rule->values[i]; + + entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; + if (f->op & AUDIT_NEGATE) + f->op |= AUDIT_NOT_EQUAL; + else if (!(f->op & AUDIT_OPERATORS)) + f->op |= AUDIT_EQUAL; + f->op &= ~AUDIT_NEGATE; } - for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i]; - return 0; + +exit_nofree: + return entry; + +exit_free: + audit_free_rule(entry); + return ERR_PTR(err); } -/* Check to see if two rules are identical. It is called from - * audit_add_rule during AUDIT_ADD and - * audit_del_rule during AUDIT_DEL. */ -static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) +/* Translate struct audit_rule_data to kernel's rule respresentation. */ +static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, + size_t datasz) { + int err = 0; + struct audit_entry *entry; + void *bufp; + /* size_t remain = datasz - sizeof(struct audit_rule_data); */ int i; - if (a->flags != b->flags) - return 1; + entry = audit_to_entry_common((struct audit_rule *)data); + if (IS_ERR(entry)) + goto exit_nofree; - if (a->action != b->action) - return 1; + bufp = data->buf; + entry->rule.vers_ops = 2; + for (i = 0; i < data->field_count; i++) { + struct audit_field *f = &entry->rule.fields[i]; + + err = -EINVAL; + if (!(data->fieldflags[i] & AUDIT_OPERATORS) || + data->fieldflags[i] & ~AUDIT_OPERATORS) + goto exit_free; + + f->op = data->fieldflags[i] & AUDIT_OPERATORS; + f->type = data->fields[i]; + switch(f->type) { + /* call type-specific conversion routines here */ + default: + f->val = data->values[i]; + } + } + +exit_nofree: + return entry; + +exit_free: + audit_free_rule(entry); + return ERR_PTR(err); +} + +/* Pack a filter field's string representation into data block. */ +static inline size_t audit_pack_string(void **bufp, char *str) +{ + size_t len = strlen(str); + + memcpy(*bufp, str, len); + *bufp += len; + + return len; +} + +/* Translate kernel rule respresentation to struct audit_rule. + * Exists for backward compatibility with userspace. */ +static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule) +{ + struct audit_rule *rule; + int i; + + rule = kmalloc(sizeof(*rule), GFP_KERNEL); + if (unlikely(!rule)) + return ERR_PTR(-ENOMEM); + memset(rule, 0, sizeof(*rule)); + + rule->flags = krule->flags | krule->listnr; + rule->action = krule->action; + rule->field_count = krule->field_count; + for (i = 0; i < rule->field_count; i++) { + rule->values[i] = krule->fields[i].val; + rule->fields[i] = krule->fields[i].type; + + if (krule->vers_ops == 1) { + if (krule->fields[i].op & AUDIT_NOT_EQUAL) + rule->fields[i] |= AUDIT_NEGATE; + } else { + rule->fields[i] |= krule->fields[i].op; + } + } + for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i]; + + return rule; +} - if (a->field_count != b->field_count) +/* Translate kernel rule respresentation to struct audit_rule_data. */ +static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) +{ + struct audit_rule_data *data; + void *bufp; + int i; + + data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL); + if (unlikely(!data)) + return ERR_PTR(-ENOMEM); + memset(data, 0, sizeof(*data)); + + data->flags = krule->flags | krule->listnr; + data->action = krule->action; + data->field_count = krule->field_count; + bufp = data->buf; + for (i = 0; i < data->field_count; i++) { + struct audit_field *f = &krule->fields[i]; + + data->fields[i] = f->type; + data->fieldflags[i] = f->op; + switch(f->type) { + /* call type-specific conversion routines here */ + default: + data->values[i] = f->val; + } + } + for (i = 0; i < AUDIT_BITMASK_SIZE; i++) data->mask[i] = krule->mask[i]; + + return data; +} + +/* Compare two rules in kernel format. Considered success if rules + * don't match. */ +static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) +{ + int i; + + if (a->flags != b->flags || + a->listnr != b->listnr || + a->action != b->action || + a->field_count != b->field_count) return 1; for (i = 0; i < a->field_count; i++) { - if (a->fields[i] != b->fields[i] - || a->values[i] != b->values[i]) + if (a->fields[i].type != b->fields[i].type || + a->fields[i].op != b->fields[i].op) return 1; + + switch(a->fields[i].type) { + /* call type-specific comparison routines here */ + default: + if (a->fields[i].val != b->fields[i].val) + return 1; + } } for (i = 0; i < AUDIT_BITMASK_SIZE; i++) @@ -95,41 +322,21 @@ static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) return 0; } -/* Note that audit_add_rule and audit_del_rule are called via - * audit_receive() in audit.c, and are protected by +/* Add rule to given filterlist if not a duplicate. Protected by * audit_netlink_sem. */ -static inline int audit_add_rule(struct audit_rule *rule, +static inline int audit_add_rule(struct audit_entry *entry, struct list_head *list) { - struct audit_entry *entry; - int i; + struct audit_entry *e; /* Do not use the _rcu iterator here, since this is the only * addition routine. */ - list_for_each_entry(entry, list, list) { - if (!audit_compare_rule(rule, &entry->rule)) + list_for_each_entry(e, list, list) { + if (!audit_compare_rule(&entry->rule, &e->rule)) return -EEXIST; } - for (i = 0; i < rule->field_count; i++) { - if (rule->fields[i] & AUDIT_UNUSED_BITS) - return -EINVAL; - if ( rule->fields[i] & AUDIT_NEGATE) - rule->fields[i] |= AUDIT_NOT_EQUAL; - else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 ) - rule->fields[i] |= AUDIT_EQUAL; - rule->fields[i] &= ~AUDIT_NEGATE; - } - - if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) - return -ENOMEM; - if (audit_copy_rule(&entry->rule, rule)) { - kfree(entry); - return -EINVAL; - } - if (entry->rule.flags & AUDIT_FILTER_PREPEND) { - entry->rule.flags &= ~AUDIT_FILTER_PREPEND; list_add_rcu(&entry->list, list); } else { list_add_tail_rcu(&entry->list, list); @@ -138,16 +345,9 @@ static inline int audit_add_rule(struct audit_rule *rule, return 0; } -static inline void audit_free_rule(struct rcu_head *head) -{ - struct audit_entry *e = container_of(head, struct audit_entry, rcu); - kfree(e); -} - -/* Note that audit_add_rule and audit_del_rule are called via - * audit_receive() in audit.c, and are protected by +/* Remove an existing rule from filterlist. Protected by * audit_netlink_sem. */ -static inline int audit_del_rule(struct audit_rule *rule, +static inline int audit_del_rule(struct audit_entry *entry, struct list_head *list) { struct audit_entry *e; @@ -155,16 +355,18 @@ static inline int audit_del_rule(struct audit_rule *rule, /* Do not use the _rcu iterator here, since this is the only * deletion routine. */ list_for_each_entry(e, list, list) { - if (!audit_compare_rule(rule, &e->rule)) { + if (!audit_compare_rule(&entry->rule, &e->rule)) { list_del_rcu(&e->list); - call_rcu(&e->rcu, audit_free_rule); + call_rcu(&e->rcu, audit_free_rule_rcu); return 0; } } return -ENOENT; /* No matching rule */ } -static int audit_list_rules(void *_dest) +/* List rules using struct audit_rule. Exists for backward + * compatibility with userspace. */ +static int audit_list(void *_dest) { int pid, seq; int *dest = _dest; @@ -180,9 +382,16 @@ static int audit_list_rules(void *_dest) /* The *_rcu iterators not needed here because we are always called with audit_netlink_sem held. */ for (i=0; irule); + if (unlikely(!rule)) + break; audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, - &entry->rule, sizeof(entry->rule)); + rule, sizeof(*rule)); + kfree(rule); + } } audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); @@ -190,6 +399,40 @@ static int audit_list_rules(void *_dest) return 0; } +/* List rules using struct audit_rule_data. */ +static int audit_list_rules(void *_dest) +{ + int pid, seq; + int *dest = _dest; + struct audit_entry *e; + int i; + + pid = dest[0]; + seq = dest[1]; + kfree(dest); + + down(&audit_netlink_sem); + + /* The *_rcu iterators not needed here because we are + always called with audit_netlink_sem held. */ + for (i=0; irule); + if (unlikely(!data)) + break; + audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, + data, sizeof(*data)); + kfree(data); + } + } + audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0); + + up(&audit_netlink_sem); + return 0; +} + /** * audit_receive_filter - apply all rules to the specified message type * @type: audit message type @@ -197,18 +440,20 @@ static int audit_list_rules(void *_dest) * @uid: target uid for netlink audit messages * @seq: netlink audit message sequence (serial) number * @data: payload data + * @datasz: size of payload data * @loginuid: loginuid of sender */ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, - uid_t loginuid) + size_t datasz, uid_t loginuid) { struct task_struct *tsk; int *dest; - int err = 0; - unsigned listnr; + int err = 0; + struct audit_entry *entry; switch (type) { case AUDIT_LIST: + case AUDIT_LIST_RULES: /* We can't just spew out the rules here because we might fill * the available socket buffer space and deadlock waiting for * auditctl to read from it... which isn't ever going to @@ -221,41 +466,48 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, dest[0] = pid; dest[1] = seq; - tsk = kthread_run(audit_list_rules, dest, "audit_list_rules"); + if (type == AUDIT_LIST) + tsk = kthread_run(audit_list, dest, "audit_list"); + else + tsk = kthread_run(audit_list_rules, dest, + "audit_list_rules"); if (IS_ERR(tsk)) { kfree(dest); err = PTR_ERR(tsk); } break; case AUDIT_ADD: - listnr = ((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; - switch(listnr) { - default: - return -EINVAL; - - case AUDIT_FILTER_USER: - case AUDIT_FILTER_TYPE: -#ifdef CONFIG_AUDITSYSCALL - case AUDIT_FILTER_ENTRY: - case AUDIT_FILTER_EXIT: - case AUDIT_FILTER_TASK: -#endif - ; - } - err = audit_add_rule(data, &audit_filter_list[listnr]); + case AUDIT_ADD_RULE: + if (type == AUDIT_ADD) + entry = audit_rule_to_entry(data); + else + entry = audit_data_to_entry(data, datasz); + if (IS_ERR(entry)) + return PTR_ERR(entry); + + err = audit_add_rule(entry, + &audit_filter_list[entry->rule.listnr]); if (!err) audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "auid=%u added an audit rule\n", loginuid); + else + audit_free_rule(entry); break; case AUDIT_DEL: - listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; - if (listnr >= AUDIT_NR_FILTERS) - return -EINVAL; - - err = audit_del_rule(data, &audit_filter_list[listnr]); + case AUDIT_DEL_RULE: + if (type == AUDIT_DEL) + entry = audit_rule_to_entry(data); + else + entry = audit_data_to_entry(data, datasz); + if (IS_ERR(entry)) + return PTR_ERR(entry); + + err = audit_del_rule(entry, + &audit_filter_list[entry->rule.listnr]); if (!err) audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "auid=%u removed an audit rule\n", loginuid); + audit_free_rule(entry); break; default: return -EINVAL; @@ -287,29 +539,27 @@ int audit_comparator(const u32 left, const u32 op, const u32 right) static int audit_filter_user_rules(struct netlink_skb_parms *cb, - struct audit_rule *rule, + struct audit_krule *rule, enum audit_state *state) { int i; for (i = 0; i < rule->field_count; i++) { - u32 field = rule->fields[i] & ~AUDIT_OPERATORS; - u32 op = rule->fields[i] & AUDIT_OPERATORS; - u32 value = rule->values[i]; + struct audit_field *f = &rule->fields[i]; int result = 0; - switch (field) { + switch (f->type) { case AUDIT_PID: - result = audit_comparator(cb->creds.pid, op, value); + result = audit_comparator(cb->creds.pid, f->op, f->val); break; case AUDIT_UID: - result = audit_comparator(cb->creds.uid, op, value); + result = audit_comparator(cb->creds.uid, f->op, f->val); break; case AUDIT_GID: - result = audit_comparator(cb->creds.gid, op, value); + result = audit_comparator(cb->creds.gid, f->op, f->val); break; case AUDIT_LOGINUID: - result = audit_comparator(cb->loginuid, op, value); + result = audit_comparator(cb->loginuid, f->op, f->val); break; } @@ -354,14 +604,11 @@ int audit_filter_type(int type) list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], list) { - struct audit_rule *rule = &e->rule; int i; - for (i = 0; i < rule->field_count; i++) { - u32 field = rule->fields[i] & ~AUDIT_OPERATORS; - u32 op = rule->fields[i] & AUDIT_OPERATORS; - u32 value = rule->values[i]; - if ( field == AUDIT_MSGTYPE ) { - result = audit_comparator(type, op, value); + for (i = 0; i < e->rule.field_count; i++) { + struct audit_field *f = &e->rule.fields[i]; + if (f->type == AUDIT_MSGTYPE) { + result = audit_comparator(type, f->op, f->val); if (!result) break; } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 17719b303638..ba0878854777 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -162,70 +162,68 @@ struct audit_context { /* Compare a task_struct with an audit_rule. Return 1 on match, 0 * otherwise. */ static int audit_filter_rules(struct task_struct *tsk, - struct audit_rule *rule, + struct audit_krule *rule, struct audit_context *ctx, enum audit_state *state) { int i, j; for (i = 0; i < rule->field_count; i++) { - u32 field = rule->fields[i] & ~AUDIT_OPERATORS; - u32 op = rule->fields[i] & AUDIT_OPERATORS; - u32 value = rule->values[i]; + struct audit_field *f = &rule->fields[i]; int result = 0; - switch (field) { + switch (f->type) { case AUDIT_PID: - result = audit_comparator(tsk->pid, op, value); + result = audit_comparator(tsk->pid, f->op, f->val); break; case AUDIT_UID: - result = audit_comparator(tsk->uid, op, value); + result = audit_comparator(tsk->uid, f->op, f->val); break; case AUDIT_EUID: - result = audit_comparator(tsk->euid, op, value); + result = audit_comparator(tsk->euid, f->op, f->val); break; case AUDIT_SUID: - result = audit_comparator(tsk->suid, op, value); + result = audit_comparator(tsk->suid, f->op, f->val); break; case AUDIT_FSUID: - result = audit_comparator(tsk->fsuid, op, value); + result = audit_comparator(tsk->fsuid, f->op, f->val); break; case AUDIT_GID: - result = audit_comparator(tsk->gid, op, value); + result = audit_comparator(tsk->gid, f->op, f->val); break; case AUDIT_EGID: - result = audit_comparator(tsk->egid, op, value); + result = audit_comparator(tsk->egid, f->op, f->val); break; case AUDIT_SGID: - result = audit_comparator(tsk->sgid, op, value); + result = audit_comparator(tsk->sgid, f->op, f->val); break; case AUDIT_FSGID: - result = audit_comparator(tsk->fsgid, op, value); + result = audit_comparator(tsk->fsgid, f->op, f->val); break; case AUDIT_PERS: - result = audit_comparator(tsk->personality, op, value); + result = audit_comparator(tsk->personality, f->op, f->val); break; case AUDIT_ARCH: if (ctx) - result = audit_comparator(ctx->arch, op, value); + result = audit_comparator(ctx->arch, f->op, f->val); break; case AUDIT_EXIT: if (ctx && ctx->return_valid) - result = audit_comparator(ctx->return_code, op, value); + result = audit_comparator(ctx->return_code, f->op, f->val); break; case AUDIT_SUCCESS: if (ctx && ctx->return_valid) { - if (value) - result = audit_comparator(ctx->return_valid, op, AUDITSC_SUCCESS); + if (f->val) + result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS); else - result = audit_comparator(ctx->return_valid, op, AUDITSC_FAILURE); + result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE); } break; case AUDIT_DEVMAJOR: if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (audit_comparator(MAJOR(ctx->names[j].dev), op, value)) { + if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) { ++result; break; } @@ -235,7 +233,7 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_DEVMINOR: if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (audit_comparator(MINOR(ctx->names[j].dev), op, value)) { + if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) { ++result; break; } @@ -245,8 +243,8 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_INODE: if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (audit_comparator(ctx->names[j].ino, op, value) || - audit_comparator(ctx->names[j].pino, op, value)) { + if (audit_comparator(ctx->names[j].ino, f->op, f->val) || + audit_comparator(ctx->names[j].pino, f->op, f->val)) { ++result; break; } @@ -256,14 +254,14 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_LOGINUID: result = 0; if (ctx) - result = audit_comparator(ctx->loginuid, op, value); + result = audit_comparator(ctx->loginuid, f->op, f->val); break; case AUDIT_ARG0: case AUDIT_ARG1: case AUDIT_ARG2: case AUDIT_ARG3: if (ctx) - result = audit_comparator(ctx->argv[field-AUDIT_ARG0], op, value); + result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val); break; } diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index d7c0e912c5f3..73158244cf8c 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -99,6 +99,9 @@ static struct nlmsg_perm nlmsg_audit_perms[] = { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_LIST_RULES, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, + { AUDIT_ADD_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, }; -- cgit v1.2.3 From 5d3301088f7e412992d9e61cc3604cbdff3090ff Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Mon, 9 Jan 2006 09:48:17 -0500 Subject: [PATCH] add/remove rule update Hi, The following patch adds a little more information to the add/remove rule message emitted by the kernel. Signed-off-by: Steve Grubb Signed-off-by: Al Viro --- include/linux/audit.h | 2 +- kernel/auditfilter.c | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index 8a3b98175c25..d760430c8de3 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -240,7 +240,7 @@ struct audit_rule_data { __u32 flags; /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */ __u32 action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */ __u32 field_count; - __u32 mask[AUDIT_BITMASK_SIZE]; + __u32 mask[AUDIT_BITMASK_SIZE]; /* syscall(s) affected */ __u32 fields[AUDIT_MAX_FIELDS]; __u32 values[AUDIT_MAX_FIELDS]; __u32 fieldflags[AUDIT_MAX_FIELDS]; diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 686d514a3518..35f8fa82bb8b 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -487,10 +487,11 @@ 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]); - if (!err) - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u added an audit rule\n", loginuid); - else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u add rule to list=%d res=%d\n", + loginuid, entry->rule.listnr, !err); + + if (err) audit_free_rule(entry); break; case AUDIT_DEL: @@ -504,9 +505,10 @@ 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]); - if (!err) - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u removed an audit rule\n", loginuid); + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u remove rule from list=%d res=%d\n", + loginuid, entry->rule.listnr, !err); + audit_free_rule(entry); break; default: -- cgit v1.2.3 From 5bdb98868062c1b14025883049551af343233187 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Sat, 3 Dec 2005 08:39:35 -0500 Subject: [PATCH] promiscuous mode Hi, When a network interface goes into promiscuous mode, its an important security issue. The attached patch is intended to capture that action and send an event to the audit system. The patch carves out a new block of numbers for kernel detected anomalies. These are events that may indicate suspicious activity. Other examples of potential kernel anomalies would be: exceeding disk quota, rlimit violations, changes to syscall entry table. Signed-off-by: Steve Grubb Signed-off-by: Al Viro --- include/linux/audit.h | 7 ++++++- net/core/dev.c | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index d760430c8de3..1c47c59058c1 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -35,7 +35,8 @@ * 1400 - 1499 SE Linux use * 1500 - 1599 kernel LSPP events * 1600 - 1699 kernel crypto events - * 1700 - 1999 future kernel use (maybe integrity labels and related events) + * 1700 - 1799 kernel anomaly records + * 1800 - 1999 future kernel use (maybe integrity labels and related events) * 2000 is for otherwise unclassified kernel audit messages (legacy) * 2001 - 2099 unused (kernel) * 2100 - 2199 user space anomaly records @@ -90,6 +91,10 @@ #define AUDIT_MAC_STATUS 1404 /* Changed enforcing,permissive,off */ #define AUDIT_MAC_CONFIG_CHANGE 1405 /* Changes to booleans */ +#define AUDIT_FIRST_KERN_ANOM_MSG 1700 +#define AUDIT_LAST_KERN_ANOM_MSG 1799 +#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */ + #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ /* Rule flags */ diff --git a/net/core/dev.c b/net/core/dev.c index 2afb0de95329..e9f84a66ce81 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -115,6 +115,7 @@ #include #endif /* CONFIG_NET_RADIO */ #include +#include /* * The list of packet types we will receive (as opposed to discard) @@ -2120,6 +2121,12 @@ void dev_set_promiscuity(struct net_device *dev, int inc) printk(KERN_INFO "device %s %s promiscuous mode\n", dev->name, (dev->flags & IFF_PROMISC) ? "entered" : "left"); + audit_log(current->audit_context, GFP_ATOMIC, + AUDIT_ANOM_PROMISCUOUS, + "dev=%s prom=%d old_prom=%d auid=%u", + dev->name, (dev->flags & IFF_PROMISC), + (old_flags & IFF_PROMISC), + audit_get_loginuid(current->audit_context)); } } -- cgit v1.2.3 From eaa82edf20d738a7ae31f4b0a5f72f64c14a58df Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 20 Mar 2006 23:24:04 -0500 Subject: SUNRPC,RPCSEC_GSS: fix krb5 sequence numbers. Use a spinlock to ensure unique sequence numbers when creating krb5 gss tokens. Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust --- include/linux/sunrpc/gss_krb5.h | 2 ++ net/sunrpc/auth_gss/gss_krb5_seal.c | 11 ++++++++--- net/sunrpc/auth_gss/gss_krb5_wrap.c | 9 ++++++--- 3 files changed, 16 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h index 2c3601d31045..1279280d7196 100644 --- a/include/linux/sunrpc/gss_krb5.h +++ b/include/linux/sunrpc/gss_krb5.h @@ -53,6 +53,8 @@ struct krb5_ctx { struct xdr_netobj mech_used; }; +extern spinlock_t krb5_seq_lock; + #define KG_TOK_MIC_MSG 0x0101 #define KG_TOK_WRAP_MSG 0x0201 diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index 58f9721980e2..f43311221a72 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -70,6 +70,8 @@ # define RPCDBG_FACILITY RPCDBG_AUTH #endif +spinlock_t krb5_seq_lock = SPIN_LOCK_UNLOCKED; + u32 gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text, struct xdr_netobj *token) @@ -80,6 +82,7 @@ gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text, struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; unsigned char *ptr, *krb5_hdr, *msg_start; s32 now; + u32 seq_send; dprintk("RPC: gss_krb5_seal\n"); @@ -134,12 +137,14 @@ gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text, BUG(); } + spin_lock(&krb5_seq_lock); + seq_send = ctx->seq_send++; + spin_unlock(&krb5_seq_lock); + if ((krb5_make_seq_num(ctx->seq, ctx->initiate ? 0 : 0xff, - ctx->seq_send, krb5_hdr + 16, krb5_hdr + 8))) + seq_send, krb5_hdr + 16, krb5_hdr + 8))) goto out_err; - ctx->seq_send++; - return ((ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE); out_err: return GSS_S_FAILURE; diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c index 346133e446cb..89d1f3e14128 100644 --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c @@ -128,6 +128,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, s32 now; int headlen; struct page **tmp_pages; + u32 seq_send; dprintk("RPC: gss_wrap_kerberos\n"); @@ -206,18 +207,20 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, BUG(); } + spin_lock(&krb5_seq_lock); + seq_send = kctx->seq_send++; + spin_unlock(&krb5_seq_lock); + /* XXX would probably be more efficient to compute checksum * and encrypt at the same time: */ if ((krb5_make_seq_num(kctx->seq, kctx->initiate ? 0 : 0xff, - kctx->seq_send, krb5_hdr + 16, krb5_hdr + 8))) + seq_send, krb5_hdr + 16, krb5_hdr + 8))) goto out_err; if (gss_encrypt_xdr_buf(kctx->enc, buf, offset + headlen - blocksize, pages)) goto out_err; - kctx->seq_send++; - return ((kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE); out_err: return GSS_S_FAILURE; -- cgit v1.2.3 From f3ee439f43381e45b191cf721b4a51d41f33301f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 20 Mar 2006 23:24:13 -0500 Subject: LOCKD: nlmsvc_traverse_blocks return is unused Note that we never return non-zero. Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust --- fs/lockd/svclock.c | 3 +-- fs/lockd/svcsubs.c | 5 +++-- include/linux/lockd/lockd.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index ce754efe2841..d2b66bad7d50 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -309,14 +309,13 @@ restart: * Loop over all blocks and perform the action specified. * (NLM_ACT_CHECK handled by nlmsvc_inspect_file). */ -int +void nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action) { if (action == NLM_ACT_MARK) nlmsvc_act_mark(host, file); else nlmsvc_act_unlock(host, file); - return 0; } /* diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 601e5b3dfe20..2043011a1a2d 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -224,8 +224,9 @@ nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action) if (file->f_count || file->f_blocks || file->f_shares) return 1; } else { - if (nlmsvc_traverse_blocks(host, file, action) - || nlmsvc_traverse_shares(host, file, action)) + nlmsvc_traverse_blocks(host, file, action); + + if (nlmsvc_traverse_shares(host, file, action)) return 1; } return nlm_traverse_locks(host, file, action); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index a04137d0c5de..995f89dc8c04 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -183,7 +183,7 @@ u32 nlmsvc_testlock(struct nlm_file *, struct nlm_lock *, struct nlm_lock *); u32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *); unsigned long nlmsvc_retry_blocked(void); -int nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, +void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, int action); void nlmsvc_grant_reply(struct svc_rqst *, struct nlm_cookie *, u32); -- cgit v1.2.3 From 5f12191bc000ea31970339a5f54c11087506711c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 20 Mar 2006 23:24:25 -0500 Subject: LOCKD: Make nlmsvc_traverse_shares return void The nlmsvc_traverse_shares return value is always zero, hence useless. Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust --- fs/lockd/svcshare.c | 4 +--- fs/lockd/svcsubs.c | 4 +--- include/linux/lockd/share.h | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c index 4943fb7836ce..27288c83da96 100644 --- a/fs/lockd/svcshare.c +++ b/fs/lockd/svcshare.c @@ -88,7 +88,7 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, * Traverse all shares for a given file (and host). * NLM_ACT_CHECK is handled by nlmsvc_inspect_file. */ -int +void nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, int action) { struct nlm_share *share, **shpp; @@ -106,6 +106,4 @@ nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, int action) } shpp = &share->s_next; } - - return 0; } diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 2043011a1a2d..c7a6e3ae44d6 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -225,9 +225,7 @@ nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action) return 1; } else { nlmsvc_traverse_blocks(host, file, action); - - if (nlmsvc_traverse_shares(host, file, action)) - return 1; + nlmsvc_traverse_shares(host, file, action); } return nlm_traverse_locks(host, file, action); } diff --git a/include/linux/lockd/share.h b/include/linux/lockd/share.h index 5d8aa325f140..c75a424ebe4c 100644 --- a/include/linux/lockd/share.h +++ b/include/linux/lockd/share.h @@ -25,6 +25,6 @@ u32 nlmsvc_share_file(struct nlm_host *, struct nlm_file *, struct nlm_args *); u32 nlmsvc_unshare_file(struct nlm_host *, struct nlm_file *, struct nlm_args *); -int nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, int); +void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, int); #endif /* LINUX_LOCKD_SHARE_H */ -- cgit v1.2.3 From 0157903e840f970c90880426505cbb8be5ddde68 Mon Sep 17 00:00:00 2001 From: "Hyok S. Choi" Date: Fri, 24 Feb 2006 21:41:25 +0000 Subject: [ARM] noMMU: removes TLB codes in nommu mode This patch removes TLB related codes in nommu mode. Signed-off-by: Hyok S. Choi Signed-off-by: Russell King --- include/asm-arm/tlb.h | 9 +++++++++ include/asm-arm/tlbflush.h | 9 +++++++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/include/asm-arm/tlb.h b/include/asm-arm/tlb.h index f49bfb78c221..cb740025d413 100644 --- a/include/asm-arm/tlb.h +++ b/include/asm-arm/tlb.h @@ -19,6 +19,14 @@ #include #include + +#ifndef CONFIG_MMU + +#include +#include + +#else /* !CONFIG_MMU */ + #include /* @@ -82,4 +90,5 @@ tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) #define tlb_migrate_finish(mm) do { } while (0) +#endif /* CONFIG_MMU */ #endif diff --git a/include/asm-arm/tlbflush.h b/include/asm-arm/tlbflush.h index 0c2acc944a0a..d64b180200fa 100644 --- a/include/asm-arm/tlbflush.h +++ b/include/asm-arm/tlbflush.h @@ -11,6 +11,13 @@ #define _ASMARM_TLBFLUSH_H #include + +#ifndef CONFIG_MMU + +#define tlb_flush(tlb) ((void) tlb) + +#else /* CONFIG_MMY */ + #include #define TLB_V3_PAGE (1 << 0) @@ -423,4 +430,6 @@ extern void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte #endif +#endif /* CONFIG_MMU */ + #endif -- cgit v1.2.3 From fb1c7762b9b1f3c53daf0e700e977d77a29bcf04 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 24 Feb 2006 21:44:56 +0000 Subject: [ARM] Fix typo in tlbflush.h s/CONFIG_MMY/CONFIG_MMU/ Signed-off-by: Russell King --- include/asm-arm/tlbflush.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-arm/tlbflush.h b/include/asm-arm/tlbflush.h index d64b180200fa..728992451dd1 100644 --- a/include/asm-arm/tlbflush.h +++ b/include/asm-arm/tlbflush.h @@ -16,7 +16,7 @@ #define tlb_flush(tlb) ((void) tlb) -#else /* CONFIG_MMY */ +#else /* CONFIG_MMU */ #include -- cgit v1.2.3 From 74945c8616a50074277e18641baaae7464006766 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 16 Mar 2006 14:44:36 +0000 Subject: [ARM] nommu: Move hardware page table definitions to pgtable-hwdef.h Move the hardware PMD and PTE page table definitions from pgtable.h into pgtable-hwdef.h, and include pgtable-hwdef.h as necessary. Signed-off-by: Russell King --- arch/arm/mach-sa1100/assabet.c | 1 + arch/arm/mm/proc-arm1020.S | 1 + arch/arm/mm/proc-arm1020e.S | 1 + arch/arm/mm/proc-arm1022.S | 1 + arch/arm/mm/proc-arm1026.S | 1 + arch/arm/mm/proc-arm6_7.S | 1 + arch/arm/mm/proc-arm720.S | 1 + arch/arm/mm/proc-arm920.S | 1 + arch/arm/mm/proc-arm922.S | 1 + arch/arm/mm/proc-arm925.S | 1 + arch/arm/mm/proc-arm926.S | 1 + arch/arm/mm/proc-sa110.S | 1 + arch/arm/mm/proc-sa1100.S | 1 + arch/arm/mm/proc-v6.S | 1 + arch/arm/mm/proc-xscale.S | 1 + include/asm-arm/pgalloc.h | 5 +++ include/asm-arm/pgtable-hwdef.h | 88 +++++++++++++++++++++++++++++++++++++++++ include/asm-arm/pgtable.h | 80 ------------------------------------- 18 files changed, 108 insertions(+), 80 deletions(-) create mode 100644 include/asm-arm/pgtable-hwdef.h (limited to 'include') diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index a599bb0d4ab8..c58f12ba7a93 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S index 82ec954e45b6..5f460d717253 100644 --- a/arch/arm/mm/proc-arm1020.S +++ b/arch/arm/mm/proc-arm1020.S @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S index 7375fe930f72..3e5ea5508839 100644 --- a/arch/arm/mm/proc-arm1020e.S +++ b/arch/arm/mm/proc-arm1020e.S @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S index 6ca639094d6f..f778545d57a2 100644 --- a/arch/arm/mm/proc-arm1022.S +++ b/arch/arm/mm/proc-arm1022.S @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S index 10317e4f55d2..148c111fde73 100644 --- a/arch/arm/mm/proc-arm1026.S +++ b/arch/arm/mm/proc-arm1026.S @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-arm6_7.S b/arch/arm/mm/proc-arm6_7.S index 8e7e1e70ab05..540359b475d0 100644 --- a/arch/arm/mm/proc-arm6_7.S +++ b/arch/arm/mm/proc-arm6_7.S @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-arm720.S b/arch/arm/mm/proc-arm720.S index a13e0184d343..72b0819326db 100644 --- a/arch/arm/mm/proc-arm720.S +++ b/arch/arm/mm/proc-arm720.S @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S index d16513899999..e57d3b8de4b7 100644 --- a/arch/arm/mm/proc-arm920.S +++ b/arch/arm/mm/proc-arm920.S @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S index 23b8ed97f4e3..3170b37f82f2 100644 --- a/arch/arm/mm/proc-arm922.S +++ b/arch/arm/mm/proc-arm922.S @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S index ee95c52db513..eb78850addaa 100644 --- a/arch/arm/mm/proc-arm925.S +++ b/arch/arm/mm/proc-arm925.S @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S index 7d042dc20c47..23ad5027d90c 100644 --- a/arch/arm/mm/proc-arm926.S +++ b/arch/arm/mm/proc-arm926.S @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S index bd330c4075a1..c916a6cae404 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mm/proc-sa1100.S b/arch/arm/mm/proc-sa1100.S index 91b89124c0d7..41f21f2dd8ff 100644 --- a/arch/arm/mm/proc-sa1100.S +++ b/arch/arm/mm/proc-sa1100.S @@ -23,6 +23,7 @@ #include #include #include +#include #include /* diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index 92f3ca31b7b9..9a7e7c096aa9 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "proc-macros.S" diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S index 2d3823ec3153..3e7e6a8f4109 100644 --- a/arch/arm/mm/proc-xscale.S +++ b/arch/arm/mm/proc-xscale.S @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/include/asm-arm/pgalloc.h b/include/asm-arm/pgalloc.h index bc18ff405181..c4ac2e67768d 100644 --- a/include/asm-arm/pgalloc.h +++ b/include/asm-arm/pgalloc.h @@ -10,10 +10,15 @@ #ifndef _ASMARM_PGALLOC_H #define _ASMARM_PGALLOC_H +#include +#include #include #include #include +#define _PAGE_USER_TABLE (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_USER)) +#define _PAGE_KERNEL_TABLE (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_KERNEL)) + /* * Since we have only two-level page tables, these are trivial */ diff --git a/include/asm-arm/pgtable-hwdef.h b/include/asm-arm/pgtable-hwdef.h new file mode 100644 index 000000000000..1d033495cc75 --- /dev/null +++ b/include/asm-arm/pgtable-hwdef.h @@ -0,0 +1,88 @@ +/* + * linux/include/asm-arm/pgtable-hwdef.h + * + * Copyright (C) 1995-2002 Russell King + * + * 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 _ASMARM_PGTABLE_HWDEF_H +#define _ASMARM_PGTABLE_HWDEF_H + +/* + * Hardware page table definitions. + * + * + Level 1 descriptor (PMD) + * - common + */ +#define PMD_TYPE_MASK (3 << 0) +#define PMD_TYPE_FAULT (0 << 0) +#define PMD_TYPE_TABLE (1 << 0) +#define PMD_TYPE_SECT (2 << 0) +#define PMD_BIT4 (1 << 4) +#define PMD_DOMAIN(x) ((x) << 5) +#define PMD_PROTECTION (1 << 9) /* v5 */ +/* + * - section + */ +#define PMD_SECT_BUFFERABLE (1 << 2) +#define PMD_SECT_CACHEABLE (1 << 3) +#define PMD_SECT_AP_WRITE (1 << 10) +#define PMD_SECT_AP_READ (1 << 11) +#define PMD_SECT_TEX(x) ((x) << 12) /* v5 */ +#define PMD_SECT_APX (1 << 15) /* v6 */ +#define PMD_SECT_S (1 << 16) /* v6 */ +#define PMD_SECT_nG (1 << 17) /* v6 */ +#define PMD_SECT_SUPER (1 << 18) /* v6 */ + +#define PMD_SECT_UNCACHED (0) +#define PMD_SECT_BUFFERED (PMD_SECT_BUFFERABLE) +#define PMD_SECT_WT (PMD_SECT_CACHEABLE) +#define PMD_SECT_WB (PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE) +#define PMD_SECT_MINICACHE (PMD_SECT_TEX(1) | PMD_SECT_CACHEABLE) +#define PMD_SECT_WBWA (PMD_SECT_TEX(1) | PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE) +#define PMD_SECT_NONSHARED_DEV (PMD_SECT_TEX(2)) + +/* + * - coarse table (not used) + */ + +/* + * + Level 2 descriptor (PTE) + * - common + */ +#define PTE_TYPE_MASK (3 << 0) +#define PTE_TYPE_FAULT (0 << 0) +#define PTE_TYPE_LARGE (1 << 0) +#define PTE_TYPE_SMALL (2 << 0) +#define PTE_TYPE_EXT (3 << 0) /* v5 */ +#define PTE_BUFFERABLE (1 << 2) +#define PTE_CACHEABLE (1 << 3) + +/* + * - extended small page/tiny page + */ +#define PTE_EXT_XN (1 << 0) /* v6 */ +#define PTE_EXT_AP_MASK (3 << 4) +#define PTE_EXT_AP0 (1 << 4) +#define PTE_EXT_AP1 (2 << 4) +#define PTE_EXT_AP_UNO_SRO (0 << 4) +#define PTE_EXT_AP_UNO_SRW (PTE_EXT_AP0) +#define PTE_EXT_AP_URO_SRW (PTE_EXT_AP1) +#define PTE_EXT_AP_URW_SRW (PTE_EXT_AP1|PTE_EXT_AP0) +#define PTE_EXT_TEX(x) ((x) << 6) /* v5 */ +#define PTE_EXT_APX (1 << 9) /* v6 */ +#define PTE_EXT_SHARED (1 << 10) /* v6 */ +#define PTE_EXT_NG (1 << 11) /* v6 */ + +/* + * - small page + */ +#define PTE_SMALL_AP_MASK (0xff << 4) +#define PTE_SMALL_AP_UNO_SRO (0x00 << 4) +#define PTE_SMALL_AP_UNO_SRW (0x55 << 4) +#define PTE_SMALL_AP_URO_SRW (0xaa << 4) +#define PTE_SMALL_AP_URW_SRW (0xff << 4) + +#endif diff --git a/include/asm-arm/pgtable.h b/include/asm-arm/pgtable.h index 70e00d08345e..e595ae24efe2 100644 --- a/include/asm-arm/pgtable.h +++ b/include/asm-arm/pgtable.h @@ -136,81 +136,6 @@ extern void __pgd_error(const char *file, int line, unsigned long val); #define SUPERSECTION_SIZE (1UL << SUPERSECTION_SHIFT) #define SUPERSECTION_MASK (~(SUPERSECTION_SIZE-1)) -/* - * Hardware page table definitions. - * - * + Level 1 descriptor (PMD) - * - common - */ -#define PMD_TYPE_MASK (3 << 0) -#define PMD_TYPE_FAULT (0 << 0) -#define PMD_TYPE_TABLE (1 << 0) -#define PMD_TYPE_SECT (2 << 0) -#define PMD_BIT4 (1 << 4) -#define PMD_DOMAIN(x) ((x) << 5) -#define PMD_PROTECTION (1 << 9) /* v5 */ -/* - * - section - */ -#define PMD_SECT_BUFFERABLE (1 << 2) -#define PMD_SECT_CACHEABLE (1 << 3) -#define PMD_SECT_AP_WRITE (1 << 10) -#define PMD_SECT_AP_READ (1 << 11) -#define PMD_SECT_TEX(x) ((x) << 12) /* v5 */ -#define PMD_SECT_APX (1 << 15) /* v6 */ -#define PMD_SECT_S (1 << 16) /* v6 */ -#define PMD_SECT_nG (1 << 17) /* v6 */ -#define PMD_SECT_SUPER (1 << 18) /* v6 */ - -#define PMD_SECT_UNCACHED (0) -#define PMD_SECT_BUFFERED (PMD_SECT_BUFFERABLE) -#define PMD_SECT_WT (PMD_SECT_CACHEABLE) -#define PMD_SECT_WB (PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE) -#define PMD_SECT_MINICACHE (PMD_SECT_TEX(1) | PMD_SECT_CACHEABLE) -#define PMD_SECT_WBWA (PMD_SECT_TEX(1) | PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE) -#define PMD_SECT_NONSHARED_DEV (PMD_SECT_TEX(2)) - -/* - * - coarse table (not used) - */ - -/* - * + Level 2 descriptor (PTE) - * - common - */ -#define PTE_TYPE_MASK (3 << 0) -#define PTE_TYPE_FAULT (0 << 0) -#define PTE_TYPE_LARGE (1 << 0) -#define PTE_TYPE_SMALL (2 << 0) -#define PTE_TYPE_EXT (3 << 0) /* v5 */ -#define PTE_BUFFERABLE (1 << 2) -#define PTE_CACHEABLE (1 << 3) - -/* - * - extended small page/tiny page - */ -#define PTE_EXT_XN (1 << 0) /* v6 */ -#define PTE_EXT_AP_MASK (3 << 4) -#define PTE_EXT_AP0 (1 << 4) -#define PTE_EXT_AP1 (2 << 4) -#define PTE_EXT_AP_UNO_SRO (0 << 4) -#define PTE_EXT_AP_UNO_SRW (PTE_EXT_AP0) -#define PTE_EXT_AP_URO_SRW (PTE_EXT_AP1) -#define PTE_EXT_AP_URW_SRW (PTE_EXT_AP1|PTE_EXT_AP0) -#define PTE_EXT_TEX(x) ((x) << 6) /* v5 */ -#define PTE_EXT_APX (1 << 9) /* v6 */ -#define PTE_EXT_SHARED (1 << 10) /* v6 */ -#define PTE_EXT_NG (1 << 11) /* v6 */ - -/* - * - small page - */ -#define PTE_SMALL_AP_MASK (0xff << 4) -#define PTE_SMALL_AP_UNO_SRO (0x00 << 4) -#define PTE_SMALL_AP_UNO_SRW (0x55 << 4) -#define PTE_SMALL_AP_URO_SRW (0xaa << 4) -#define PTE_SMALL_AP_URW_SRW (0xff << 4) - /* * "Linux" PTE definitions. * @@ -236,11 +161,6 @@ extern void __pgd_error(const char *file, int line, unsigned long val); #ifndef __ASSEMBLY__ -#include - -#define _PAGE_USER_TABLE (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_USER)) -#define _PAGE_KERNEL_TABLE (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_KERNEL)) - /* * The following macros handle the cache and bufferable bits... */ -- cgit v1.2.3 From 2332c9ae7911618575241e0c843cd686968db8e3 Mon Sep 17 00:00:00 2001 From: "Chen, Kenneth W" Date: Wed, 22 Mar 2006 10:49:00 -0800 Subject: [IA64] fix ia64 is_hugepage_only_range fix is_hugepage_only_range() definition to be "overlaps" instead of "within architectural restricted hugetlb address range". Simplify the ia64 specific code that used to use is_hugepage_only_range() to just check which region the address is in. Signed-off-by: Ken Chen Signed-off-by: Tony Luck --- arch/ia64/mm/hugetlbpage.c | 7 +++---- include/asm-ia64/page.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c index 9dbc7dadd165..8d506710fdbd 100644 --- a/arch/ia64/mm/hugetlbpage.c +++ b/arch/ia64/mm/hugetlbpage.c @@ -113,8 +113,7 @@ void hugetlb_free_pgd_range(struct mmu_gather **tlb, unsigned long floor, unsigned long ceiling) { /* - * This is called only when is_hugepage_only_range(addr,), - * and it follows that is_hugepage_only_range(end,) also. + * This is called to free hugetlb page tables. * * The offset of these addresses from the base of the hugetlb * region must be scaled down by HPAGE_SIZE/PAGE_SIZE so that @@ -126,9 +125,9 @@ void hugetlb_free_pgd_range(struct mmu_gather **tlb, addr = htlbpage_to_page(addr); end = htlbpage_to_page(end); - if (is_hugepage_only_range(tlb->mm, floor, HPAGE_SIZE)) + if (REGION_NUMBER(floor) == RGN_HPAGE) floor = htlbpage_to_page(floor); - if (is_hugepage_only_range(tlb->mm, ceiling, HPAGE_SIZE)) + if (REGION_NUMBER(ceiling) == RGN_HPAGE) ceiling = htlbpage_to_page(ceiling); free_pgd_range(tlb, addr, end, floor, ceiling); diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h index 3ab27333dae4..6e9aa23250c4 100644 --- a/include/asm-ia64/page.h +++ b/include/asm-ia64/page.h @@ -149,7 +149,7 @@ typedef union ia64_va { | (REGION_OFFSET(x) >> (HPAGE_SHIFT-PAGE_SHIFT))) # define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) # define is_hugepage_only_range(mm, addr, len) \ - (REGION_NUMBER(addr) == RGN_HPAGE && \ + (REGION_NUMBER(addr) == RGN_HPAGE || \ REGION_NUMBER((addr)+(len)-1) == RGN_HPAGE) extern unsigned int hpage_shift; #endif -- cgit v1.2.3 From 244fd54540806a5e3391d117794105a35815cbb2 Mon Sep 17 00:00:00 2001 From: "Chen, Kenneth W" Date: Sun, 12 Mar 2006 09:00:13 -0800 Subject: [IA64] add init declaration to cpu initialization functions Add init declaration to cpu initialization functions. Signed-off-by: Ken Chen Signed-off-by: Tony Luck --- arch/ia64/kernel/setup.c | 12 ++++++------ arch/ia64/mm/contig.c | 2 +- arch/ia64/mm/discontig.c | 2 +- include/asm-ia64/processor.h | 1 - 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 3258e09278d0..340dc0362fe5 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -362,7 +362,7 @@ mark_bsp_online (void) } #ifdef CONFIG_SMP -static void +static void __init check_for_logical_procs (void) { pal_logical_to_physical_t info; @@ -623,7 +623,7 @@ struct seq_operations cpuinfo_op = { .show = show_cpuinfo }; -void +static void __cpuinit identify_cpu (struct cpuinfo_ia64 *c) { union { @@ -700,7 +700,7 @@ setup_per_cpu_areas (void) * In addition, the minimum of the i-cache stride sizes is calculated for * "flush_icache_range()". */ -static void +static void __cpuinit get_max_cacheline_size (void) { unsigned long line_size, max = 1; @@ -763,10 +763,10 @@ get_max_cacheline_size (void) * cpu_init() initializes state that is per-CPU. This function acts * as a 'CPU state barrier', nothing should get across. */ -void +void __cpuinit cpu_init (void) { - extern void __devinit ia64_mmu_init (void *); + extern void __cpuinit ia64_mmu_init (void *); unsigned long num_phys_stacked; pal_vm_info_2_u_t vmi; unsigned int max_ctx; @@ -894,7 +894,7 @@ void sched_cacheflush(void) ia64_sal_cache_flush(3); } -void +void __init check_bugs (void) { ia64_patch_mckinley_e9((unsigned long) __start___mckinley_e9_bundles, diff --git a/arch/ia64/mm/contig.c b/arch/ia64/mm/contig.c index 9855ba318094..8abb9e86d8aa 100644 --- a/arch/ia64/mm/contig.c +++ b/arch/ia64/mm/contig.c @@ -176,7 +176,7 @@ find_memory (void) * * Allocate and setup per-cpu data areas. */ -void * +void * __cpuinit per_cpu_init (void) { void *cpu_data; diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 573d5cc63e2b..2f5e44862e91 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -525,7 +525,7 @@ void __init find_memory(void) * find_pernode_space() does most of this already, we just need to set * local_per_cpu_offset */ -void *per_cpu_init(void) +void __cpuinit *per_cpu_init(void) { int cpu; static int first_time = 1; diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h index 128fefd8056f..b3bd58e80690 100644 --- a/include/asm-ia64/processor.h +++ b/include/asm-ia64/processor.h @@ -181,7 +181,6 @@ DECLARE_PER_CPU(struct cpuinfo_ia64, cpu_info); #define local_cpu_data (&__ia64_per_cpu_var(cpu_info)) #define cpu_data(cpu) (&per_cpu(cpu_info, cpu)) -extern void identify_cpu (struct cpuinfo_ia64 *); extern void print_cpu_info (struct cpuinfo_ia64 *); typedef struct { -- cgit v1.2.3 From 9c42954dfd50d02963cd453fb84bfef3967af2f0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 23 Mar 2006 16:59:37 +0000 Subject: [ARM] Move enable_irq and disable_irq to assembler.h 5d25ac038a317d454a4321cba955f756400835a5 broke VFP builds due to enable_irq not being defined as an assembly macro. Move it to assembler.h so everyone can use it. Signed-off-by: Russell King --- arch/arm/kernel/entry-header.S | 18 ------------------ arch/arm/vfp/entry.S | 1 + include/asm-arm/assembler.h | 27 ++++++++++++++++++++++----- 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index 55c99cdab7d6..f1c2fd5b63e4 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -37,24 +37,6 @@ #endif .endm -#if __LINUX_ARM_ARCH__ >= 6 - .macro disable_irq - cpsid i - .endm - - .macro enable_irq - cpsie i - .endm -#else - .macro disable_irq - msr cpsr_c, #PSR_I_BIT | SVC_MODE - .endm - - .macro enable_irq - msr cpsr_c, #SVC_MODE - .endm -#endif - .macro get_thread_info, rd mov \rd, sp, lsr #13 mov \rd, \rd, lsl #13 diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S index 9ab1abfbe7ad..7b595547c1c8 100644 --- a/arch/arm/vfp/entry.S +++ b/arch/arm/vfp/entry.S @@ -18,6 +18,7 @@ #include #include #include +#include #include .globl do_vfp diff --git a/include/asm-arm/assembler.h b/include/asm-arm/assembler.h index f31ac92b6c7f..d53bafa9bf1c 100644 --- a/include/asm-arm/assembler.h +++ b/include/asm-arm/assembler.h @@ -80,16 +80,33 @@ instr regs /* - * Save the current IRQ state and disable IRQs. Note that this macro - * assumes FIQs are enabled, and that the processor is in SVC mode. + * Enable and disable interrupts */ - .macro save_and_disable_irqs, oldcpsr - mrs \oldcpsr, cpsr #if __LINUX_ARM_ARCH__ >= 6 + .macro disable_irq cpsid i + .endm + + .macro enable_irq + cpsie i + .endm #else - msr cpsr_c, #PSR_I_BIT | MODE_SVC + .macro disable_irq + msr cpsr_c, #PSR_I_BIT | SVC_MODE + .endm + + .macro enable_irq + msr cpsr_c, #SVC_MODE + .endm #endif + +/* + * Save the current IRQ state and disable IRQs. Note that this macro + * assumes FIQs are enabled, and that the processor is in SVC mode. + */ + .macro save_and_disable_irqs, oldcpsr + mrs \oldcpsr, cpsr + disable_irq .endm /* -- cgit v1.2.3 From a54123e27779049d27d21e6c8adfee73aa2c0734 Mon Sep 17 00:00:00 2001 From: Bob Breuer Date: Thu, 23 Mar 2006 22:36:19 -0800 Subject: [SPARC]: Try to start getting SMP back into shape. Todo items: - IRQ_INPROGRESS flag - use sparc64 irq buckets, or generic irq_desc? - sun4d - re-indent large chunks of sun4m_smp.c - some places assume sequential cpu numbering (i.e. 0,1 instead of 0,2) Last I checked (with 2.6.14), random programs segfault with dual HyperSPARC. And with SuperSPARC II's, it seems stable but will eventually die from a write lock error (wrong lock owner or something). I haven't tried the HyperSPARC + highmem combination recently, so that may still be a problem. Signed-off-by: David S. Miller --- arch/sparc/Kconfig | 1 - arch/sparc/kernel/irq.c | 66 ++++++++------- arch/sparc/kernel/smp.c | 84 ++++++++++++++----- arch/sparc/kernel/sparc_ksyms.c | 4 - arch/sparc/kernel/sun4d_irq.c | 2 +- arch/sparc/kernel/sun4d_smp.c | 8 +- arch/sparc/kernel/sun4m_smp.c | 181 +++++++++++++++++----------------------- arch/sparc/mm/srmmu.c | 6 ++ include/asm-sparc/cpudata.h | 1 + include/asm-sparc/smp.h | 9 +- include/asm-sparc/spinlock.h | 25 +++++- 11 files changed, 209 insertions(+), 178 deletions(-) (limited to 'include') diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index f944b58cdfe7..7c58fc1a39c4 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -23,7 +23,6 @@ menu "General machine setup" config SMP bool "Symmetric multi-processing support (does not work on sun4/sun4c)" - depends on BROKEN ---help--- This enables support for systems with more than one CPU. If you have a system with only one CPU, say N. If you have a system with more diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c index 4c60a6ef54a9..aac8af5aae51 100644 --- a/arch/sparc/kernel/irq.c +++ b/arch/sparc/kernel/irq.c @@ -154,9 +154,11 @@ void (*sparc_init_timers)(irqreturn_t (*)(int, void *,struct pt_regs *)) = struct irqaction static_irqaction[MAX_STATIC_ALLOC]; int static_irq_count; -struct irqaction *irq_action[NR_IRQS] = { - [0 ... (NR_IRQS-1)] = NULL -}; +struct { + struct irqaction *action; + int flags; +} sparc_irq[NR_IRQS]; +#define SPARC_IRQ_INPROGRESS 1 /* Used to protect the IRQ action lists */ DEFINE_SPINLOCK(irq_action_lock); @@ -177,7 +179,7 @@ int show_interrupts(struct seq_file *p, void *v) } spin_lock_irqsave(&irq_action_lock, flags); if (i < NR_IRQS) { - action = *(i + irq_action); + action = sparc_irq[i].action; if (!action) goto out_unlock; seq_printf(p, "%3d: ", i); @@ -186,7 +188,7 @@ int show_interrupts(struct seq_file *p, void *v) #else for_each_online_cpu(j) { seq_printf(p, "%10u ", - kstat_cpu(cpu_logical_map(j)).irqs[i]); + kstat_cpu(j).irqs[i]); } #endif seq_printf(p, " %c %s", @@ -207,7 +209,7 @@ out_unlock: void free_irq(unsigned int irq, void *dev_id) { struct irqaction * action; - struct irqaction * tmp = NULL; + struct irqaction **actionp; unsigned long flags; unsigned int cpu_irq; @@ -225,7 +227,8 @@ void free_irq(unsigned int irq, void *dev_id) spin_lock_irqsave(&irq_action_lock, flags); - action = *(cpu_irq + irq_action); + actionp = &sparc_irq[cpu_irq].action; + action = *actionp; if (!action->handler) { printk("Trying to free free IRQ%d\n",irq); @@ -235,7 +238,7 @@ void free_irq(unsigned int irq, void *dev_id) for (; action; action = action->next) { if (action->dev_id == dev_id) break; - tmp = action; + actionp = &action->next; } if (!action) { printk("Trying to free free shared IRQ%d\n",irq); @@ -254,11 +257,8 @@ void free_irq(unsigned int irq, void *dev_id) irq, action->name); goto out_unlock; } - - if (action && tmp) - tmp->next = action->next; - else - *(cpu_irq + irq_action) = action->next; + + *actionp = action->next; spin_unlock_irqrestore(&irq_action_lock, flags); @@ -268,7 +268,7 @@ void free_irq(unsigned int irq, void *dev_id) kfree(action); - if (!(*(cpu_irq + irq_action))) + if (!sparc_irq[cpu_irq].action) disable_irq(irq); out_unlock: @@ -287,8 +287,11 @@ EXPORT_SYMBOL(free_irq); #ifdef CONFIG_SMP void synchronize_irq(unsigned int irq) { - printk("synchronize_irq says: implement me!\n"); - BUG(); + unsigned int cpu_irq; + + cpu_irq = irq & (NR_IRQS - 1); + while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS) + cpu_relax(); } #endif /* SMP */ @@ -299,7 +302,7 @@ void unexpected_irq(int irq, void *dev_id, struct pt_regs * regs) unsigned int cpu_irq; cpu_irq = irq & (NR_IRQS - 1); - action = *(cpu_irq + irq_action); + action = sparc_irq[cpu_irq].action; printk("IO device interrupt, irq = %d\n", irq); printk("PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc, @@ -330,7 +333,8 @@ void handler_irq(int irq, struct pt_regs * regs) if(irq < 10) smp4m_irq_rotate(cpu); #endif - action = *(irq + irq_action); + action = sparc_irq[irq].action; + sparc_irq[irq].flags |= SPARC_IRQ_INPROGRESS; kstat_cpu(cpu).irqs[irq]++; do { if (!action || !action->handler) @@ -338,6 +342,7 @@ void handler_irq(int irq, struct pt_regs * regs) action->handler(irq, action->dev_id, regs); action = action->next; } while (action); + sparc_irq[irq].flags &= ~SPARC_IRQ_INPROGRESS; enable_pil_irq(irq); irq_exit(); } @@ -389,7 +394,7 @@ int request_fast_irq(unsigned int irq, spin_lock_irqsave(&irq_action_lock, flags); - action = *(cpu_irq + irq_action); + action = sparc_irq[cpu_irq].action; if(action) { if(action->flags & SA_SHIRQ) panic("Trying to register fast irq when already shared.\n"); @@ -452,7 +457,7 @@ int request_fast_irq(unsigned int irq, action->dev_id = NULL; action->next = NULL; - *(cpu_irq + irq_action) = action; + sparc_irq[cpu_irq].action = action; enable_irq(irq); @@ -467,7 +472,7 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) { - struct irqaction * action, *tmp = NULL; + struct irqaction * action, **actionp; unsigned long flags; unsigned int cpu_irq; int ret; @@ -490,20 +495,20 @@ int request_irq(unsigned int irq, spin_lock_irqsave(&irq_action_lock, flags); - action = *(cpu_irq + irq_action); + actionp = &sparc_irq[cpu_irq].action; + action = *actionp; if (action) { - if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) { - for (tmp = action; tmp->next; tmp = tmp->next); - } else { + if (!(action->flags & SA_SHIRQ) || !(irqflags & SA_SHIRQ)) { ret = -EBUSY; goto out_unlock; } - if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) { + if ((action->flags & SA_INTERRUPT) != (irqflags & SA_INTERRUPT)) { printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq); ret = -EBUSY; goto out_unlock; - } - action = NULL; /* Or else! */ + } + for ( ; action; action = *actionp) + actionp = &action->next; } /* If this is flagged as statically allocated then we use our @@ -532,10 +537,7 @@ int request_irq(unsigned int irq, action->next = NULL; action->dev_id = dev_id; - if (tmp) - tmp->next = action; - else - *(cpu_irq + irq_action) = action; + *actionp = action; enable_irq(irq); diff --git a/arch/sparc/kernel/smp.c b/arch/sparc/kernel/smp.c index ea5682ce7031..2be812115197 100644 --- a/arch/sparc/kernel/smp.c +++ b/arch/sparc/kernel/smp.c @@ -45,6 +45,7 @@ volatile int __cpu_logical_map[NR_CPUS]; cpumask_t cpu_online_map = CPU_MASK_NONE; cpumask_t phys_cpu_present_map = CPU_MASK_NONE; +cpumask_t smp_commenced_mask = CPU_MASK_NONE; /* The only guaranteed locking primitive available on all Sparc * processors is 'ldstub [%reg + immediate], %dest_reg' which atomically @@ -57,11 +58,6 @@ cpumask_t phys_cpu_present_map = CPU_MASK_NONE; /* Used to make bitops atomic */ unsigned char bitops_spinlock = 0; -volatile unsigned long ipi_count; - -volatile int smp_process_available=0; -volatile int smp_commenced = 0; - void __init smp_store_cpu_info(int id) { int cpu_node; @@ -79,6 +75,22 @@ void __init smp_store_cpu_info(int id) void __init smp_cpus_done(unsigned int max_cpus) { + extern void smp4m_smp_done(void); + unsigned long bogosum = 0; + int cpu, num; + + for (cpu = 0, num = 0; cpu < NR_CPUS; cpu++) + if (cpu_online(cpu)) { + num++; + bogosum += cpu_data(cpu).udelay_val; + } + + printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n", + num, bogosum/(500000/HZ), + (bogosum/(5000/HZ))%100); + + BUG_ON(sparc_cpu_model != sun4m); + smp4m_smp_done(); } void cpu_panic(void) @@ -89,17 +101,6 @@ void cpu_panic(void) struct linux_prom_registers smp_penguin_ctable __initdata = { 0 }; -void __init smp_boot_cpus(void) -{ - extern void smp4m_boot_cpus(void); - extern void smp4d_boot_cpus(void); - - if (sparc_cpu_model == sun4m) - smp4m_boot_cpus(); - else - smp4d_boot_cpus(); -} - void smp_send_reschedule(int cpu) { /* See sparc64 */ @@ -252,20 +253,61 @@ int setup_profiling_timer(unsigned int multiplier) return 0; } -void __init smp_prepare_cpus(unsigned int maxcpus) +void __init smp_prepare_cpus(unsigned int max_cpus) { + extern void smp4m_boot_cpus(void); + int i, cpuid, ncpus, extra; + + BUG_ON(sparc_cpu_model != sun4m); + printk("Entering SMP Mode...\n"); + + ncpus = 1; + extra = 0; + for (i = 0; !cpu_find_by_instance(i, NULL, &cpuid); i++) { + if (cpuid == boot_cpu_id) + continue; + if (cpuid < NR_CPUS && ncpus++ < max_cpus) + cpu_set(cpuid, phys_cpu_present_map); + else + extra++; + } + if (max_cpus >= NR_CPUS && extra) + printk("Warning: NR_CPUS is too low to start all cpus\n"); + + smp_store_cpu_info(boot_cpu_id); + + smp4m_boot_cpus(); } void __devinit smp_prepare_boot_cpu(void) { - current_thread_info()->cpu = hard_smp_processor_id(); - cpu_set(smp_processor_id(), cpu_online_map); - cpu_set(smp_processor_id(), phys_cpu_present_map); + int cpuid = hard_smp_processor_id(); + + if (cpuid >= NR_CPUS) { + prom_printf("Serious problem, boot cpu id >= NR_CPUS\n"); + prom_halt(); + } + if (cpuid != 0) + printk("boot cpu id != 0, this could work but is untested\n"); + + current_thread_info()->cpu = cpuid; + cpu_set(cpuid, cpu_online_map); + cpu_set(cpuid, phys_cpu_present_map); } int __devinit __cpu_up(unsigned int cpu) { - panic("smp doesn't work\n"); + extern int smp4m_boot_one_cpu(int); + int ret; + + ret = smp4m_boot_one_cpu(cpu); + + if (!ret) { + cpu_set(cpu, smp_commenced_mask); + while (!cpu_online(cpu)) + mb(); + } + return ret; } void smp_bogo(struct seq_file *m) diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 19b25399d7e4..2c21d7907635 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -136,10 +136,6 @@ EXPORT_PER_CPU_SYMBOL(__cpu_data); /* IRQ implementation. */ EXPORT_SYMBOL(synchronize_irq); -/* Misc SMP information */ -EXPORT_SYMBOL(__cpu_number_map); -EXPORT_SYMBOL(__cpu_logical_map); - /* CPU online map and active count. */ EXPORT_SYMBOL(cpu_online_map); EXPORT_SYMBOL(phys_cpu_present_map); diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index cea7fc6fc6e5..ca656d9bd6fd 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -54,7 +54,7 @@ unsigned char cpu_leds[32]; unsigned char sbus_tid[32]; #endif -extern struct irqaction *irq_action[]; +static struct irqaction *irq_action[NR_IRQS]; extern spinlock_t irq_action_lock; struct sbus_action { diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 41bb9596be48..b141b7ee6717 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -46,14 +46,16 @@ extern volatile int smp_processors_ready; extern int smp_num_cpus; static int smp_highest_cpu; extern volatile unsigned long cpu_callin_map[NR_CPUS]; -extern struct cpuinfo_sparc cpu_data[NR_CPUS]; +extern cpuinfo_sparc cpu_data[NR_CPUS]; extern unsigned char boot_cpu_id; extern int smp_activated; extern volatile int __cpu_number_map[NR_CPUS]; extern volatile int __cpu_logical_map[NR_CPUS]; extern volatile unsigned long ipi_count; extern volatile int smp_process_available; -extern volatile int smp_commenced; + +extern cpumask_t smp_commenced_mask; + extern int __smp4d_processor_id(void); /* #define SMP_DEBUG */ @@ -136,7 +138,7 @@ void __init smp4d_callin(void) local_irq_enable(); /* We don't allow PIL 14 yet */ - while(!smp_commenced) + while (!cpu_isset(cpuid, smp_commenced_mask)) barrier(); spin_lock_irqsave(&sun4d_imsk_lock, flags); diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 1dde312eebda..70b375a4c2c2 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -40,15 +40,11 @@ extern ctxd_t *srmmu_ctx_table_phys; extern void calibrate_delay(void); extern volatile int smp_processors_ready; -extern int smp_num_cpus; extern volatile unsigned long cpu_callin_map[NR_CPUS]; extern unsigned char boot_cpu_id; -extern int smp_activated; -extern volatile int __cpu_number_map[NR_CPUS]; -extern volatile int __cpu_logical_map[NR_CPUS]; -extern volatile unsigned long ipi_count; -extern volatile int smp_process_available; -extern volatile int smp_commenced; + +extern cpumask_t smp_commenced_mask; + extern int __smp4m_processor_id(void); /*#define SMP_DEBUG*/ @@ -77,8 +73,6 @@ void __init smp4m_callin(void) local_flush_cache_all(); local_flush_tlb_all(); - set_irq_udt(boot_cpu_id); - /* Get our local ticker going. */ smp_setup_percpu_timer(); @@ -95,8 +89,9 @@ void __init smp4m_callin(void) * to call the scheduler code. */ /* Allow master to continue. */ - swap((unsigned long *)&cpu_callin_map[cpuid], 1); + swap(&cpu_callin_map[cpuid], 1); + /* XXX: What's up with all the flushes? */ local_flush_cache_all(); local_flush_tlb_all(); @@ -111,13 +106,14 @@ void __init smp4m_callin(void) atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; - while(!smp_commenced) - barrier(); - - local_flush_cache_all(); - local_flush_tlb_all(); + while (!cpu_isset(cpuid, smp_commenced_mask)) + mb(); local_irq_enable(); + + cpu_set(cpuid, cpu_online_map); + /* last one in gets all the interrupts (for testing) */ + set_irq_udt(boot_cpu_id); } extern void init_IRQ(void); @@ -134,102 +130,76 @@ extern unsigned long trapbase_cpu3[]; void __init smp4m_boot_cpus(void) { - int cpucount = 0; - int i, mid; + smp_setup_percpu_timer(); + local_flush_cache_all(); +} - printk("Entering SMP Mode...\n"); +int smp4m_boot_one_cpu(int i) +{ + extern unsigned long sun4m_cpu_startup; + unsigned long *entry = &sun4m_cpu_startup; + struct task_struct *p; + int timeout; + int cpu_node; - local_irq_enable(); - cpus_clear(cpu_present_map); + cpu_find_by_mid(i, &cpu_node); + + /* Cook up an idler for this guy. */ + p = fork_idle(i); + current_set[i] = task_thread_info(p); + /* See trampoline.S for details... */ + entry += ((i-1) * 3); - for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++) - cpu_set(mid, cpu_present_map); + /* + * Initialize the contexts table + * Since the call to prom_startcpu() trashes the structure, + * we need to re-initialize it for each cpu + */ + smp_penguin_ctable.which_io = 0; + smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys; + smp_penguin_ctable.reg_size = 0; - for(i=0; i < NR_CPUS; i++) { - __cpu_number_map[i] = -1; - __cpu_logical_map[i] = -1; + /* whirrr, whirrr, whirrrrrrrrr... */ + printk("Starting CPU %d at %p\n", i, entry); + local_flush_cache_all(); + prom_startcpu(cpu_node, + &smp_penguin_ctable, 0, (char *)entry); + + /* wheee... it's going... */ + for(timeout = 0; timeout < 10000; timeout++) { + if(cpu_callin_map[i]) + break; + udelay(200); } - __cpu_number_map[boot_cpu_id] = 0; - __cpu_logical_map[0] = boot_cpu_id; - current_thread_info()->cpu = boot_cpu_id; + if (!(cpu_callin_map[i])) { + printk("Processor %d is stuck.\n", i); + return -ENODEV; + } - smp_store_cpu_info(boot_cpu_id); - set_irq_udt(boot_cpu_id); - smp_setup_percpu_timer(); local_flush_cache_all(); - if(cpu_find_by_instance(1, NULL, NULL)) - return; /* Not an MP box. */ - for(i = 0; i < NR_CPUS; i++) { - if(i == boot_cpu_id) - continue; - - if (cpu_isset(i, cpu_present_map)) { - extern unsigned long sun4m_cpu_startup; - unsigned long *entry = &sun4m_cpu_startup; - struct task_struct *p; - int timeout; - - /* Cook up an idler for this guy. */ - p = fork_idle(i); - cpucount++; - current_set[i] = task_thread_info(p); - /* See trampoline.S for details... */ - entry += ((i-1) * 3); - - /* - * Initialize the contexts table - * Since the call to prom_startcpu() trashes the structure, - * we need to re-initialize it for each cpu - */ - smp_penguin_ctable.which_io = 0; - smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys; - smp_penguin_ctable.reg_size = 0; - - /* whirrr, whirrr, whirrrrrrrrr... */ - printk("Starting CPU %d at %p\n", i, entry); - local_flush_cache_all(); - prom_startcpu(cpu_data(i).prom_node, - &smp_penguin_ctable, 0, (char *)entry); - - /* wheee... it's going... */ - for(timeout = 0; timeout < 10000; timeout++) { - if(cpu_callin_map[i]) - break; - udelay(200); - } - if(cpu_callin_map[i]) { - /* Another "Red Snapper". */ - __cpu_number_map[i] = i; - __cpu_logical_map[i] = i; - } else { - cpucount--; - printk("Processor %d is stuck.\n", i); - } - } - if(!(cpu_callin_map[i])) { - cpu_clear(i, cpu_present_map); - __cpu_number_map[i] = -1; + return 0; +} + +void __init smp4m_smp_done(void) +{ + int i, first; + int *prev; + + /* setup cpu list for irq rotation */ + first = 0; + prev = &first; + for (i = 0; i < NR_CPUS; i++) { + if (cpu_online(i)) { + *prev = i; + prev = &cpu_data(i).next; } } + *prev = first; local_flush_cache_all(); - if(cpucount == 0) { - printk("Error: only one Processor found.\n"); - cpu_present_map = cpumask_of_cpu(smp_processor_id()); - } else { - unsigned long bogosum = 0; - for_each_present_cpu(i) - bogosum += cpu_data(i).udelay_val; - printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", - cpucount + 1, - bogosum/(500000/HZ), - (bogosum/(5000/HZ))%100); - smp_activated = 1; - smp_num_cpus = cpucount + 1; - } /* Free unneeded trap tables */ - if (!cpu_isset(i, cpu_present_map)) { + if (!cpu_isset(1, cpu_present_map)) { ClearPageReserved(virt_to_page(trapbase_cpu1)); init_page_count(virt_to_page(trapbase_cpu1)); free_page((unsigned long)trapbase_cpu1); @@ -263,6 +233,9 @@ void __init smp4m_boot_cpus(void) */ void smp4m_irq_rotate(int cpu) { + int next = cpu_data(cpu).next; + if (next != cpu) + set_irq_udt(next); } /* Cross calls, in order to work efficiently and atomically do all @@ -289,7 +262,7 @@ void smp4m_message_pass(int target, int msg, unsigned long data, int wait) smp_cpu_in_msg[me]++; if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) { - mask = cpu_present_map; + mask = cpu_online_map; if(target == MSG_ALL_BUT_SELF) cpu_clear(me, mask); for(i = 0; i < 4; i++) { @@ -314,8 +287,8 @@ static struct smp_funcall { unsigned long arg3; unsigned long arg4; unsigned long arg5; - unsigned long processors_in[NR_CPUS]; /* Set when ipi entered. */ - unsigned long processors_out[NR_CPUS]; /* Set when ipi exited. */ + unsigned long processors_in[SUN4M_NCPUS]; /* Set when ipi entered. */ + unsigned long processors_out[SUN4M_NCPUS]; /* Set when ipi exited. */ } ccall_info; static DEFINE_SPINLOCK(cross_call_lock); @@ -324,8 +297,7 @@ static DEFINE_SPINLOCK(cross_call_lock); void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { - if(smp_processors_ready) { - register int ncpus = smp_num_cpus; + register int ncpus = SUN4M_NCPUS; unsigned long flags; spin_lock_irqsave(&cross_call_lock, flags); @@ -340,7 +312,7 @@ void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2, /* Init receive/complete mapping, plus fire the IPI's off. */ { - cpumask_t mask = cpu_present_map; + cpumask_t mask = cpu_online_map; register int i; cpu_clear(smp_processor_id(), mask); @@ -373,7 +345,6 @@ void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2, } spin_unlock_irqrestore(&cross_call_lock, flags); - } } /* Running cross calls. */ diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 27b0e0ba8581..58c65cc8d0d3 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1302,7 +1302,12 @@ void __init srmmu_paging_init(void) flush_cache_all(); srmmu_set_ctable_ptr((unsigned long)srmmu_ctx_table_phys); +#ifdef CONFIG_SMP + /* Stop from hanging here... */ + local_flush_tlb_all(); +#else flush_tlb_all(); +#endif poke_srmmu(); #ifdef CONFIG_SUN_IO @@ -1419,6 +1424,7 @@ static void __init init_vac_layout(void) max_size = vac_cache_size; if(vac_line_size < min_line_size) min_line_size = vac_line_size; + //FIXME: cpus not contiguous!! cpu++; if (cpu >= NR_CPUS || !cpu_online(cpu)) break; diff --git a/include/asm-sparc/cpudata.h b/include/asm-sparc/cpudata.h index ec0d9ef90a3b..a2c4d51d36c4 100644 --- a/include/asm-sparc/cpudata.h +++ b/include/asm-sparc/cpudata.h @@ -18,6 +18,7 @@ typedef struct { unsigned int counter; int prom_node; int mid; + int next; } cpuinfo_sparc; DECLARE_PER_CPU(cpuinfo_sparc, __cpu_data); diff --git a/include/asm-sparc/smp.h b/include/asm-sparc/smp.h index 580c51d011df..98c46e3fbe8a 100644 --- a/include/asm-sparc/smp.h +++ b/include/asm-sparc/smp.h @@ -81,16 +81,9 @@ static inline int smp_call_function(void (*func)(void *info), void *info, int no return 0; } -extern __volatile__ int __cpu_number_map[NR_CPUS]; -extern __volatile__ int __cpu_logical_map[NR_CPUS]; - static inline int cpu_logical_map(int cpu) { - return __cpu_logical_map[cpu]; -} -static inline int cpu_number_map(int cpu) -{ - return __cpu_number_map[cpu]; + return cpu; } static inline int hard_smp4m_processor_id(void) diff --git a/include/asm-sparc/spinlock.h b/include/asm-sparc/spinlock.h index e344c98a6f5f..3350c90c7869 100644 --- a/include/asm-sparc/spinlock.h +++ b/include/asm-sparc/spinlock.h @@ -94,7 +94,7 @@ static inline void __read_lock(raw_rwlock_t *rw) #define __raw_read_lock(lock) \ do { unsigned long flags; \ local_irq_save(flags); \ - __raw_read_lock(lock); \ + __read_lock(lock); \ local_irq_restore(flags); \ } while(0) @@ -114,11 +114,11 @@ static inline void __read_unlock(raw_rwlock_t *rw) #define __raw_read_unlock(lock) \ do { unsigned long flags; \ local_irq_save(flags); \ - __raw_read_unlock(lock); \ + __read_unlock(lock); \ local_irq_restore(flags); \ } while(0) -extern __inline__ void __raw_write_lock(raw_rwlock_t *rw) +static inline void __raw_write_lock(raw_rwlock_t *rw) { register raw_rwlock_t *lp asm("g1"); lp = rw; @@ -131,9 +131,28 @@ extern __inline__ void __raw_write_lock(raw_rwlock_t *rw) : "g2", "g4", "memory", "cc"); } +static inline int __raw_write_trylock(raw_rwlock_t *rw) +{ + unsigned int val; + + __asm__ __volatile__("ldstub [%1 + 3], %0" + : "=r" (val) + : "r" (&rw->lock) + : "memory"); + + if (val == 0) { + val = rw->lock & ~0xff; + if (val) + ((volatile u8*)&rw->lock)[3] = 0; + } + + return (val == 0); +} + #define __raw_write_unlock(rw) do { (rw)->lock = 0; } while(0) #define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) +#define __raw_read_trylock(lock) generic__raw_read_trylock(lock) #endif /* !(__ASSEMBLY__) */ -- cgit v1.2.3 From d4b7780ea1d2e08410fcc9963a57254147ae577a Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Fri, 24 Mar 2006 11:50:17 +0200 Subject: [PATCH] AT91RM9200 Ethernet driver This patch adds support for the Ethernet controller integrated in the Atmel AT91RM9200 SoC processor. Changes since the previous submission (01/02/2006) are: - Make use of the clk.h clock infrastructure. - The multicast hash function is not crc32. [Patch by Pedro Perez] Signed-off-by: Andrew Victor Signed-off-by: Jeff Garzik --- drivers/net/arm/Kconfig | 8 + drivers/net/arm/Makefile | 1 + drivers/net/arm/at91_ether.c | 1110 +++++++++++++++++++++ drivers/net/arm/at91_ether.h | 101 ++ include/asm-arm/arch-at91rm9200/at91rm9200_emac.h | 138 +++ 5 files changed, 1358 insertions(+) create mode 100644 drivers/net/arm/at91_ether.c create mode 100644 drivers/net/arm/at91_ether.h create mode 100644 include/asm-arm/arch-at91rm9200/at91rm9200_emac.h (limited to 'include') diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig index 625184b65e38..77fe20dbea35 100644 --- a/drivers/net/arm/Kconfig +++ b/drivers/net/arm/Kconfig @@ -31,3 +31,11 @@ config ARM_ETHERH help If you have an Acorn system with one of these network cards, you should say Y to this option if you wish to use it with Linux. + +config ARM_AT91_ETHER + tristate "AT91RM9200 Ethernet support" + depends on NET_ETHERNET && ARM && ARCH_AT91RM9200 + select MII + help + If you wish to compile a kernel for the AT91RM9200 and enable + ethernet support, then you should always answer Y to this. diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile index bc263edf06a7..42c95b79c261 100644 --- a/drivers/net/arm/Makefile +++ b/drivers/net/arm/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o obj-$(CONFIG_ARM_ETHERH) += etherh.o obj-$(CONFIG_ARM_ETHER3) += ether3.o obj-$(CONFIG_ARM_ETHER1) += ether1.o +obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c new file mode 100644 index 000000000000..5503dc8a66e4 --- /dev/null +++ b/drivers/net/arm/at91_ether.c @@ -0,0 +1,1110 @@ +/* + * Ethernet driver for the Atmel AT91RM9200 (Thunder) + * + * Copyright (C) 2003 SAN People (Pty) Ltd + * + * Based on an earlier Atmel EMAC macrocell driver by Atmel and Lineo Inc. + * Initial version by Rick Bronson 01/11/2003 + * + * Intel LXT971A PHY support by Christopher Bahns & David Knickerbocker + * (Polaroid Corporation) + * + * Realtek RTL8201(B)L PHY support by Roman Avramenko + * + * 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 +#include +#include + +#include "at91_ether.h" + +#define DRV_NAME "at91_ether" +#define DRV_VERSION "1.0" + +static struct net_device *at91_dev; +static struct clk *ether_clk; + +/* ..................................................................... */ + +/* + * Read from a EMAC register. + */ +static inline unsigned long at91_emac_read(unsigned int reg) +{ + void __iomem *emac_base = (void __iomem *)AT91_VA_BASE_EMAC; + + return __raw_readl(emac_base + reg); +} + +/* + * Write to a EMAC register. + */ +static inline void at91_emac_write(unsigned int reg, unsigned long value) +{ + void __iomem *emac_base = (void __iomem *)AT91_VA_BASE_EMAC; + + __raw_writel(value, emac_base + reg); +} + +/* ........................... PHY INTERFACE ........................... */ + +/* + * Enable the MDIO bit in MAC control register + * When not called from an interrupt-handler, access to the PHY must be + * protected by a spinlock. + */ +static void enable_mdi(void) +{ + unsigned long ctl; + + ctl = at91_emac_read(AT91_EMAC_CTL); + at91_emac_write(AT91_EMAC_CTL, ctl | AT91_EMAC_MPE); /* enable management port */ +} + +/* + * Disable the MDIO bit in the MAC control register + */ +static void disable_mdi(void) +{ + unsigned long ctl; + + ctl = at91_emac_read(AT91_EMAC_CTL); + at91_emac_write(AT91_EMAC_CTL, ctl & ~AT91_EMAC_MPE); /* disable management port */ +} + +/* + * Wait until the PHY operation is complete. + */ +static inline void at91_phy_wait(void) { + unsigned long timeout = jiffies + 2; + + while (!(at91_emac_read(AT91_EMAC_SR) & AT91_EMAC_SR_IDLE)) { + if (time_after(jiffies, timeout)) { + printk("at91_ether: MIO timeout\n"); + break; + } + cpu_relax(); + } +} + +/* + * Write value to the a PHY register + * Note: MDI interface is assumed to already have been enabled. + */ +static void write_phy(unsigned char phy_addr, unsigned char address, unsigned int value) +{ + at91_emac_write(AT91_EMAC_MAN, AT91_EMAC_MAN_802_3 | AT91_EMAC_RW_W + | ((phy_addr & 0x1f) << 23) | (address << 18) | (value & AT91_EMAC_DATA)); + + /* Wait until IDLE bit in Network Status register is cleared */ + at91_phy_wait(); +} + +/* + * Read value stored in a PHY register. + * Note: MDI interface is assumed to already have been enabled. + */ +static void read_phy(unsigned char phy_addr, unsigned char address, unsigned int *value) +{ + at91_emac_write(AT91_EMAC_MAN, AT91_EMAC_MAN_802_3 | AT91_EMAC_RW_R + | ((phy_addr & 0x1f) << 23) | (address << 18)); + + /* Wait until IDLE bit in Network Status register is cleared */ + at91_phy_wait(); + + *value = at91_emac_read(AT91_EMAC_MAN) & AT91_EMAC_DATA; +} + +/* ........................... PHY MANAGEMENT .......................... */ + +/* + * Access the PHY to determine the current link speed and mode, and update the + * MAC accordingly. + * If no link or auto-negotiation is busy, then no changes are made. + */ +static void update_linkspeed(struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned int bmsr, bmcr, lpa, mac_cfg; + unsigned int speed, duplex; + + if (!mii_link_ok(&lp->mii)) { /* no link */ + netif_carrier_off(dev); + printk(KERN_INFO "%s: Link down.\n", dev->name); + return; + } + + /* Link up, or auto-negotiation still in progress */ + read_phy(lp->phy_address, MII_BMSR, &bmsr); + read_phy(lp->phy_address, MII_BMCR, &bmcr); + if (bmcr & BMCR_ANENABLE) { /* AutoNegotiation is enabled */ + if (!(bmsr & BMSR_ANEGCOMPLETE)) + return; /* Do nothing - another interrupt generated when negotiation complete */ + + read_phy(lp->phy_address, MII_LPA, &lpa); + if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) speed = SPEED_100; + else speed = SPEED_10; + if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) duplex = DUPLEX_FULL; + else duplex = DUPLEX_HALF; + } else { + speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; + duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; + } + + /* Update the MAC */ + mac_cfg = at91_emac_read(AT91_EMAC_CFG) & ~(AT91_EMAC_SPD | AT91_EMAC_FD); + if (speed == SPEED_100) { + if (duplex == DUPLEX_FULL) /* 100 Full Duplex */ + mac_cfg |= AT91_EMAC_SPD | AT91_EMAC_FD; + else /* 100 Half Duplex */ + mac_cfg |= AT91_EMAC_SPD; + } else { + if (duplex == DUPLEX_FULL) /* 10 Full Duplex */ + mac_cfg |= AT91_EMAC_FD; + else {} /* 10 Half Duplex */ + } + at91_emac_write(AT91_EMAC_CFG, mac_cfg); + + printk(KERN_INFO "%s: Link now %i-%s\n", dev->name, speed, (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); + netif_carrier_on(dev); +} + +/* + * Handle interrupts from the PHY + */ +static irqreturn_t at91ether_phy_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned int phy; + + /* + * This hander is triggered on both edges, but the PHY chips expect + * level-triggering. We therefore have to check if the PHY actually has + * an IRQ pending. + */ + enable_mdi(); + if ((lp->phy_type == MII_DM9161_ID) || (lp->phy_type == MII_DM9161A_ID)) { + read_phy(lp->phy_address, MII_DSINTR_REG, &phy); /* ack interrupt in Davicom PHY */ + if (!(phy & (1 << 0))) + goto done; + } + else if (lp->phy_type == MII_LXT971A_ID) { + read_phy(lp->phy_address, MII_ISINTS_REG, &phy); /* ack interrupt in Intel PHY */ + if (!(phy & (1 << 2))) + goto done; + } + else if (lp->phy_type == MII_BCM5221_ID) { + read_phy(lp->phy_address, MII_BCMINTR_REG, &phy); /* ack interrupt in Broadcom PHY */ + if (!(phy & (1 << 0))) + goto done; + } + else if (lp->phy_type == MII_KS8721_ID) { + read_phy(lp->phy_address, MII_TPISTATUS, &phy); /* ack interrupt in Micrel PHY */ + if (!(phy & ((1 << 2) | 1))) + goto done; + } + + update_linkspeed(dev); + +done: + disable_mdi(); + + return IRQ_HANDLED; +} + +/* + * Initialize and enable the PHY interrupt for link-state changes + */ +static void enable_phyirq(struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned int dsintr, irq_number; + int status; + + if (lp->phy_type == MII_RTL8201_ID) /* RTL8201 does not have an interrupt */ + return; + if (lp->phy_type == MII_DP83847_ID) /* DP83847 does not have an interrupt */ + return; + if (lp->phy_type == MII_AC101L_ID) /* AC101L interrupt not supported yet */ + return; + + irq_number = lp->board_data.phy_irq_pin; + status = request_irq(irq_number, at91ether_phy_interrupt, 0, dev->name, dev); + if (status) { + printk(KERN_ERR "at91_ether: PHY IRQ %d request failed - status %d!\n", irq_number, status); + return; + } + + spin_lock_irq(&lp->lock); + enable_mdi(); + + if ((lp->phy_type == MII_DM9161_ID) || (lp->phy_type == MII_DM9161A_ID)) { /* for Davicom PHY */ + read_phy(lp->phy_address, MII_DSINTR_REG, &dsintr); + dsintr = dsintr & ~0xf00; /* clear bits 8..11 */ + write_phy(lp->phy_address, MII_DSINTR_REG, dsintr); + } + else if (lp->phy_type == MII_LXT971A_ID) { /* for Intel PHY */ + read_phy(lp->phy_address, MII_ISINTE_REG, &dsintr); + dsintr = dsintr | 0xf2; /* set bits 1, 4..7 */ + write_phy(lp->phy_address, MII_ISINTE_REG, dsintr); + } + else if (lp->phy_type == MII_BCM5221_ID) { /* for Broadcom PHY */ + dsintr = (1 << 15) | ( 1 << 14); + write_phy(lp->phy_address, MII_BCMINTR_REG, dsintr); + } + else if (lp->phy_type == MII_KS8721_ID) { /* for Micrel PHY */ + dsintr = (1 << 10) | ( 1 << 8); + write_phy(lp->phy_address, MII_TPISTATUS, dsintr); + } + + disable_mdi(); + spin_unlock_irq(&lp->lock); +} + +/* + * Disable the PHY interrupt + */ +static void disable_phyirq(struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned int dsintr; + unsigned int irq_number; + + if (lp->phy_type == MII_RTL8201_ID) /* RTL8201 does not have an interrupt */ + return; + if (lp->phy_type == MII_DP83847_ID) /* DP83847 does not have an interrupt */ + return; + if (lp->phy_type == MII_AC101L_ID) /* AC101L interrupt not supported yet */ + return; + + spin_lock_irq(&lp->lock); + enable_mdi(); + + if ((lp->phy_type == MII_DM9161_ID) || (lp->phy_type == MII_DM9161A_ID)) { /* for Davicom PHY */ + read_phy(lp->phy_address, MII_DSINTR_REG, &dsintr); + dsintr = dsintr | 0xf00; /* set bits 8..11 */ + write_phy(lp->phy_address, MII_DSINTR_REG, dsintr); + } + else if (lp->phy_type == MII_LXT971A_ID) { /* for Intel PHY */ + read_phy(lp->phy_address, MII_ISINTE_REG, &dsintr); + dsintr = dsintr & ~0xf2; /* clear bits 1, 4..7 */ + write_phy(lp->phy_address, MII_ISINTE_REG, dsintr); + } + else if (lp->phy_type == MII_BCM5221_ID) { /* for Broadcom PHY */ + read_phy(lp->phy_address, MII_BCMINTR_REG, &dsintr); + dsintr = ~(1 << 14); + write_phy(lp->phy_address, MII_BCMINTR_REG, dsintr); + } + else if (lp->phy_type == MII_KS8721_ID) { /* for Micrel PHY */ + read_phy(lp->phy_address, MII_TPISTATUS, &dsintr); + dsintr = ~((1 << 10) | (1 << 8)); + write_phy(lp->phy_address, MII_TPISTATUS, dsintr); + } + + disable_mdi(); + spin_unlock_irq(&lp->lock); + + irq_number = lp->board_data.phy_irq_pin; + free_irq(irq_number, dev); /* Free interrupt handler */ +} + +/* + * Perform a software reset of the PHY. + */ +#if 0 +static void reset_phy(struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned int bmcr; + + spin_lock_irq(&lp->lock); + enable_mdi(); + + /* Perform PHY reset */ + write_phy(lp->phy_address, MII_BMCR, BMCR_RESET); + + /* Wait until PHY reset is complete */ + do { + read_phy(lp->phy_address, MII_BMCR, &bmcr); + } while (!(bmcr && BMCR_RESET)); + + disable_mdi(); + spin_unlock_irq(&lp->lock); +} +#endif + +/* ......................... ADDRESS MANAGEMENT ........................ */ + +/* + * NOTE: Your bootloader must always set the MAC address correctly before + * booting into Linux. + * + * - It must always set the MAC address after reset, even if it doesn't + * happen to access the Ethernet while it's booting. Some versions of + * U-Boot on the AT91RM9200-DK do not do this. + * + * - Likewise it must store the addresses in the correct byte order. + * MicroMonitor (uMon) on the CSB337 does this incorrectly (and + * continues to do so, for bug-compatibility). + */ + +static short __init unpack_mac_address(struct net_device *dev, unsigned int hi, unsigned int lo) +{ + char addr[6]; + + if (machine_is_csb337()) { + addr[5] = (lo & 0xff); /* The CSB337 bootloader stores the MAC the wrong-way around */ + addr[4] = (lo & 0xff00) >> 8; + addr[3] = (lo & 0xff0000) >> 16; + addr[2] = (lo & 0xff000000) >> 24; + addr[1] = (hi & 0xff); + addr[0] = (hi & 0xff00) >> 8; + } + else { + addr[0] = (lo & 0xff); + addr[1] = (lo & 0xff00) >> 8; + addr[2] = (lo & 0xff0000) >> 16; + addr[3] = (lo & 0xff000000) >> 24; + addr[4] = (hi & 0xff); + addr[5] = (hi & 0xff00) >> 8; + } + + if (is_valid_ether_addr(addr)) { + memcpy(dev->dev_addr, &addr, 6); + return 1; + } + return 0; +} + +/* + * Set the ethernet MAC address in dev->dev_addr + */ +static void __init get_mac_address(struct net_device *dev) +{ + /* Check Specific-Address 1 */ + if (unpack_mac_address(dev, at91_emac_read(AT91_EMAC_SA1H), at91_emac_read(AT91_EMAC_SA1L))) + return; + /* Check Specific-Address 2 */ + if (unpack_mac_address(dev, at91_emac_read(AT91_EMAC_SA2H), at91_emac_read(AT91_EMAC_SA2L))) + return; + /* Check Specific-Address 3 */ + if (unpack_mac_address(dev, at91_emac_read(AT91_EMAC_SA3H), at91_emac_read(AT91_EMAC_SA3L))) + return; + /* Check Specific-Address 4 */ + if (unpack_mac_address(dev, at91_emac_read(AT91_EMAC_SA4H), at91_emac_read(AT91_EMAC_SA4L))) + return; + + printk(KERN_ERR "at91_ether: Your bootloader did not configure a MAC address.\n"); +} + +/* + * Program the hardware MAC address from dev->dev_addr. + */ +static void update_mac_address(struct net_device *dev) +{ + at91_emac_write(AT91_EMAC_SA1L, (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) | (dev->dev_addr[1] << 8) | (dev->dev_addr[0])); + at91_emac_write(AT91_EMAC_SA1H, (dev->dev_addr[5] << 8) | (dev->dev_addr[4])); + + at91_emac_write(AT91_EMAC_SA2L, 0); + at91_emac_write(AT91_EMAC_SA2H, 0); +} + +/* + * Store the new hardware address in dev->dev_addr, and update the MAC. + */ +static int set_mac_address(struct net_device *dev, void* addr) +{ + struct sockaddr *address = addr; + + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + update_mac_address(dev); + + printk("%s: Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + + return 0; +} + +static int inline hash_bit_value(int bitnr, __u8 *addr) +{ + if (addr[bitnr / 8] & (1 << (bitnr % 8))) + return 1; + return 0; +} + +/* + * The hash address register is 64 bits long and takes up two locations in the memory map. + * The least significant bits are stored in EMAC_HSL and the most significant + * bits in EMAC_HSH. + * + * The unicast hash enable and the multicast hash enable bits in the network configuration + * register enable the reception of hash matched frames. The destination address is + * reduced to a 6 bit index into the 64 bit hash register using the following hash function. + * The hash function is an exclusive or of every sixth bit of the destination address. + * hash_index[5] = da[5] ^ da[11] ^ da[17] ^ da[23] ^ da[29] ^ da[35] ^ da[41] ^ da[47] + * hash_index[4] = da[4] ^ da[10] ^ da[16] ^ da[22] ^ da[28] ^ da[34] ^ da[40] ^ da[46] + * hash_index[3] = da[3] ^ da[09] ^ da[15] ^ da[21] ^ da[27] ^ da[33] ^ da[39] ^ da[45] + * hash_index[2] = da[2] ^ da[08] ^ da[14] ^ da[20] ^ da[26] ^ da[32] ^ da[38] ^ da[44] + * hash_index[1] = da[1] ^ da[07] ^ da[13] ^ da[19] ^ da[25] ^ da[31] ^ da[37] ^ da[43] + * hash_index[0] = da[0] ^ da[06] ^ da[12] ^ da[18] ^ da[24] ^ da[30] ^ da[36] ^ da[42] + * da[0] represents the least significant bit of the first byte received, that is, the multicast/ + * unicast indicator, and da[47] represents the most significant bit of the last byte + * received. + * If the hash index points to a bit that is set in the hash register then the frame will be + * matched according to whether the frame is multicast or unicast. + * A multicast match will be signalled if the multicast hash enable bit is set, da[0] is 1 and + * the hash index points to a bit set in the hash register. + * A unicast match will be signalled if the unicast hash enable bit is set, da[0] is 0 and the + * hash index points to a bit set in the hash register. + * To receive all multicast frames, the hash register should be set with all ones and the + * multicast hash enable bit should be set in the network configuration register. + */ + +/* + * Return the hash index value for the specified address. + */ +static int hash_get_index(__u8 *addr) +{ + int i, j, bitval; + int hash_index = 0; + + for (j = 0; j < 6; j++) { + for (i = 0, bitval = 0; i < 8; i++) + bitval ^= hash_bit_value(i*6 + j, addr); + + hash_index |= (bitval << j); + } + + return hash_index; +} + +/* + * Add multicast addresses to the internal multicast-hash table. + */ +static void at91ether_sethashtable(struct net_device *dev) +{ + struct dev_mc_list *curr; + unsigned long mc_filter[2]; + unsigned int i, bitnr; + + mc_filter[0] = mc_filter[1] = 0; + + curr = dev->mc_list; + for (i = 0; i < dev->mc_count; i++, curr = curr->next) { + if (!curr) break; /* unexpected end of list */ + + bitnr = hash_get_index(curr->dmi_addr); + mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); + } + + at91_emac_write(AT91_EMAC_HSH, mc_filter[0]); + at91_emac_write(AT91_EMAC_HSL, mc_filter[1]); +} + +/* + * Enable/Disable promiscuous and multicast modes. + */ +static void at91ether_set_rx_mode(struct net_device *dev) +{ + unsigned long cfg; + + cfg = at91_emac_read(AT91_EMAC_CFG); + + if (dev->flags & IFF_PROMISC) /* Enable promiscuous mode */ + cfg |= AT91_EMAC_CAF; + else if (dev->flags & (~IFF_PROMISC)) /* Disable promiscuous mode */ + cfg &= ~AT91_EMAC_CAF; + + if (dev->flags & IFF_ALLMULTI) { /* Enable all multicast mode */ + at91_emac_write(AT91_EMAC_HSH, -1); + at91_emac_write(AT91_EMAC_HSL, -1); + cfg |= AT91_EMAC_MTI; + } else if (dev->mc_count > 0) { /* Enable specific multicasts */ + at91ether_sethashtable(dev); + cfg |= AT91_EMAC_MTI; + } else if (dev->flags & (~IFF_ALLMULTI)) { /* Disable all multicast mode */ + at91_emac_write(AT91_EMAC_HSH, 0); + at91_emac_write(AT91_EMAC_HSL, 0); + cfg &= ~AT91_EMAC_MTI; + } + + at91_emac_write(AT91_EMAC_CFG, cfg); +} + + +/* ......................... ETHTOOL SUPPORT ........................... */ + + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + unsigned int value; + + read_phy(phy_id, location, &value); + return value; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, int value) +{ + write_phy(phy_id, location, value); +} + +static int at91ether_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + int ret; + + spin_lock_irq(&lp->lock); + enable_mdi(); + + ret = mii_ethtool_gset(&lp->mii, cmd); + + disable_mdi(); + spin_unlock_irq(&lp->lock); + + if (lp->phy_media == PORT_FIBRE) { /* override media type since mii.c doesn't know */ + cmd->supported = SUPPORTED_FIBRE; + cmd->port = PORT_FIBRE; + } + + return ret; +} + +static int at91ether_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + int ret; + + spin_lock_irq(&lp->lock); + enable_mdi(); + + ret = mii_ethtool_sset(&lp->mii, cmd); + + disable_mdi(); + spin_unlock_irq(&lp->lock); + + return ret; +} + +static int at91ether_nwayreset(struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + int ret; + + spin_lock_irq(&lp->lock); + enable_mdi(); + + ret = mii_nway_restart(&lp->mii); + + disable_mdi(); + spin_unlock_irq(&lp->lock); + + return ret; +} + +static void at91ether_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev->class_dev.dev->bus_id, sizeof(info->bus_info)); +} + +static struct ethtool_ops at91ether_ethtool_ops = { + .get_settings = at91ether_get_settings, + .set_settings = at91ether_set_settings, + .get_drvinfo = at91ether_get_drvinfo, + .nway_reset = at91ether_nwayreset, + .get_link = ethtool_op_get_link, +}; + + +/* ................................ MAC ................................ */ + +/* + * Initialize and start the Receiver and Transmit subsystems + */ +static void at91ether_start(struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + struct recv_desc_bufs *dlist, *dlist_phys; + int i; + unsigned long ctl; + + dlist = lp->dlist; + dlist_phys = lp->dlist_phys; + + for (i = 0; i < MAX_RX_DESCR; i++) { + dlist->descriptors[i].addr = (unsigned int) &dlist_phys->recv_buf[i][0]; + dlist->descriptors[i].size = 0; + } + + /* Set the Wrap bit on the last descriptor */ + dlist->descriptors[i-1].addr |= EMAC_DESC_WRAP; + + /* Reset buffer index */ + lp->rxBuffIndex = 0; + + /* Program address of descriptor list in Rx Buffer Queue register */ + at91_emac_write(AT91_EMAC_RBQP, (unsigned long) dlist_phys); + + /* Enable Receive and Transmit */ + ctl = at91_emac_read(AT91_EMAC_CTL); + at91_emac_write(AT91_EMAC_CTL, ctl | AT91_EMAC_RE | AT91_EMAC_TE); +} + +/* + * Open the ethernet interface + */ +static int at91ether_open(struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned long ctl; + + if (!is_valid_ether_addr(dev->dev_addr)) + return -EADDRNOTAVAIL; + + clk_enable(ether_clk); /* Re-enable Peripheral clock */ + + /* Clear internal statistics */ + ctl = at91_emac_read(AT91_EMAC_CTL); + at91_emac_write(AT91_EMAC_CTL, ctl | AT91_EMAC_CSR); + + /* Update the MAC address (incase user has changed it) */ + update_mac_address(dev); + + /* Enable PHY interrupt */ + enable_phyirq(dev); + + /* Enable MAC interrupts */ + at91_emac_write(AT91_EMAC_IER, AT91_EMAC_RCOM | AT91_EMAC_RBNA + | AT91_EMAC_TUND | AT91_EMAC_RTRY | AT91_EMAC_TCOM + | AT91_EMAC_ROVR | AT91_EMAC_ABT); + + /* Determine current link speed */ + spin_lock_irq(&lp->lock); + enable_mdi(); + update_linkspeed(dev); + disable_mdi(); + spin_unlock_irq(&lp->lock); + + at91ether_start(dev); + netif_start_queue(dev); + return 0; +} + +/* + * Close the interface + */ +static int at91ether_close(struct net_device *dev) +{ + unsigned long ctl; + + /* Disable Receiver and Transmitter */ + ctl = at91_emac_read(AT91_EMAC_CTL); + at91_emac_write(AT91_EMAC_CTL, ctl & ~(AT91_EMAC_TE | AT91_EMAC_RE)); + + /* Disable PHY interrupt */ + disable_phyirq(dev); + + /* Disable MAC interrupts */ + at91_emac_write(AT91_EMAC_IDR, AT91_EMAC_RCOM | AT91_EMAC_RBNA + | AT91_EMAC_TUND | AT91_EMAC_RTRY | AT91_EMAC_TCOM + | AT91_EMAC_ROVR | AT91_EMAC_ABT); + + netif_stop_queue(dev); + + clk_disable(ether_clk); /* Disable Peripheral clock */ + + return 0; +} + +/* + * Transmit packet. + */ +static int at91ether_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + + if (at91_emac_read(AT91_EMAC_TSR) & AT91_EMAC_TSR_BNQ) { + netif_stop_queue(dev); + + /* Store packet information (to free when Tx completed) */ + lp->skb = skb; + lp->skb_length = skb->len; + lp->skb_physaddr = dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE); + lp->stats.tx_bytes += skb->len; + + /* Set address of the data in the Transmit Address register */ + at91_emac_write(AT91_EMAC_TAR, lp->skb_physaddr); + /* Set length of the packet in the Transmit Control register */ + at91_emac_write(AT91_EMAC_TCR, skb->len); + + dev->trans_start = jiffies; + } else { + printk(KERN_ERR "at91_ether.c: at91ether_tx() called, but device is busy!\n"); + return 1; /* if we return anything but zero, dev.c:1055 calls kfree_skb(skb) + on this skb, he also reports -ENETDOWN and printk's, so either + we free and return(0) or don't free and return 1 */ + } + + return 0; +} + +/* + * Update the current statistics from the internal statistics registers. + */ +static struct net_device_stats *at91ether_stats(struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + int ale, lenerr, seqe, lcol, ecol; + + if (netif_running(dev)) { + lp->stats.rx_packets += at91_emac_read(AT91_EMAC_OK); /* Good frames received */ + ale = at91_emac_read(AT91_EMAC_ALE); + lp->stats.rx_frame_errors += ale; /* Alignment errors */ + lenerr = at91_emac_read(AT91_EMAC_ELR) + at91_emac_read(AT91_EMAC_USF); + lp->stats.rx_length_errors += lenerr; /* Excessive Length or Undersize Frame error */ + seqe = at91_emac_read(AT91_EMAC_SEQE); + lp->stats.rx_crc_errors += seqe; /* CRC error */ + lp->stats.rx_fifo_errors += at91_emac_read(AT91_EMAC_DRFC); /* Receive buffer not available */ + lp->stats.rx_errors += (ale + lenerr + seqe + + at91_emac_read(AT91_EMAC_CDE) + at91_emac_read(AT91_EMAC_RJB)); + + lp->stats.tx_packets += at91_emac_read(AT91_EMAC_FRA); /* Frames successfully transmitted */ + lp->stats.tx_fifo_errors += at91_emac_read(AT91_EMAC_TUE); /* Transmit FIFO underruns */ + lp->stats.tx_carrier_errors += at91_emac_read(AT91_EMAC_CSE); /* Carrier Sense errors */ + lp->stats.tx_heartbeat_errors += at91_emac_read(AT91_EMAC_SQEE);/* Heartbeat error */ + + lcol = at91_emac_read(AT91_EMAC_LCOL); + ecol = at91_emac_read(AT91_EMAC_ECOL); + lp->stats.tx_window_errors += lcol; /* Late collisions */ + lp->stats.tx_aborted_errors += ecol; /* 16 collisions */ + + lp->stats.collisions += (at91_emac_read(AT91_EMAC_SCOL) + at91_emac_read(AT91_EMAC_MCOL) + lcol + ecol); + } + return &lp->stats; +} + +/* + * Extract received frame from buffer descriptors and sent to upper layers. + * (Called from interrupt context) + */ +static void at91ether_rx(struct net_device *dev) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + struct recv_desc_bufs *dlist; + unsigned char *p_recv; + struct sk_buff *skb; + unsigned int pktlen; + + dlist = lp->dlist; + while (dlist->descriptors[lp->rxBuffIndex].addr & EMAC_DESC_DONE) { + p_recv = dlist->recv_buf[lp->rxBuffIndex]; + pktlen = dlist->descriptors[lp->rxBuffIndex].size & 0x7ff; /* Length of frame including FCS */ + skb = alloc_skb(pktlen + 2, GFP_ATOMIC); + if (skb != NULL) { + skb_reserve(skb, 2); + memcpy(skb_put(skb, pktlen), p_recv, pktlen); + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->len = pktlen; + dev->last_rx = jiffies; + lp->stats.rx_bytes += pktlen; + netif_rx(skb); + } + else { + lp->stats.rx_dropped += 1; + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); + } + + if (dlist->descriptors[lp->rxBuffIndex].size & EMAC_MULTICAST) + lp->stats.multicast++; + + dlist->descriptors[lp->rxBuffIndex].addr &= ~EMAC_DESC_DONE; /* reset ownership bit */ + if (lp->rxBuffIndex == MAX_RX_DESCR-1) /* wrap after last buffer */ + lp->rxBuffIndex = 0; + else + lp->rxBuffIndex++; + } +} + +/* + * MAC interrupt handler + */ +static irqreturn_t at91ether_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned long intstatus, ctl; + + /* MAC Interrupt Status register indicates what interrupts are pending. + It is automatically cleared once read. */ + intstatus = at91_emac_read(AT91_EMAC_ISR); + + if (intstatus & AT91_EMAC_RCOM) /* Receive complete */ + at91ether_rx(dev); + + if (intstatus & AT91_EMAC_TCOM) { /* Transmit complete */ + /* The TCOM bit is set even if the transmission failed. */ + if (intstatus & (AT91_EMAC_TUND | AT91_EMAC_RTRY)) + lp->stats.tx_errors += 1; + + if (lp->skb) { + dev_kfree_skb_irq(lp->skb); + lp->skb = NULL; + dma_unmap_single(NULL, lp->skb_physaddr, lp->skb_length, DMA_TO_DEVICE); + } + netif_wake_queue(dev); + } + + /* Work-around for Errata #11 */ + if (intstatus & AT91_EMAC_RBNA) { + ctl = at91_emac_read(AT91_EMAC_CTL); + at91_emac_write(AT91_EMAC_CTL, ctl & ~AT91_EMAC_RE); + at91_emac_write(AT91_EMAC_CTL, ctl | AT91_EMAC_RE); + } + + if (intstatus & AT91_EMAC_ROVR) + printk("%s: ROVR error\n", dev->name); + + return IRQ_HANDLED; +} + +/* + * Initialize the ethernet interface + */ +static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_address, struct platform_device *pdev) +{ + struct at91_eth_data *board_data = pdev->dev.platform_data; + struct net_device *dev; + struct at91_private *lp; + unsigned int val; + int res; + + if (at91_dev) /* already initialized */ + return 0; + + dev = alloc_etherdev(sizeof(struct at91_private)); + if (!dev) + return -ENOMEM; + + dev->base_addr = AT91_VA_BASE_EMAC; + dev->irq = AT91_ID_EMAC; + SET_MODULE_OWNER(dev); + + /* Install the interrupt handler */ + if (request_irq(dev->irq, at91ether_interrupt, 0, dev->name, dev)) { + free_netdev(dev); + return -EBUSY; + } + + /* Allocate memory for DMA Receive descriptors */ + lp = (struct at91_private *)dev->priv; + lp->dlist = (struct recv_desc_bufs *) dma_alloc_coherent(NULL, sizeof(struct recv_desc_bufs), (dma_addr_t *) &lp->dlist_phys, GFP_KERNEL); + if (lp->dlist == NULL) { + free_irq(dev->irq, dev); + free_netdev(dev); + return -ENOMEM; + } + lp->board_data = *board_data; + platform_set_drvdata(pdev, dev); + + spin_lock_init(&lp->lock); + + ether_setup(dev); + dev->open = at91ether_open; + dev->stop = at91ether_close; + dev->hard_start_xmit = at91ether_tx; + dev->get_stats = at91ether_stats; + dev->set_multicast_list = at91ether_set_rx_mode; + dev->set_mac_address = set_mac_address; + dev->ethtool_ops = &at91ether_ethtool_ops; + + SET_NETDEV_DEV(dev, &pdev->dev); + + get_mac_address(dev); /* Get ethernet address and store it in dev->dev_addr */ + update_mac_address(dev); /* Program ethernet address into MAC */ + + at91_emac_write(AT91_EMAC_CTL, 0); + + if (lp->board_data.is_rmii) + at91_emac_write(AT91_EMAC_CFG, AT91_EMAC_CLK_DIV32 | AT91_EMAC_BIG | AT91_EMAC_RMII); + else + at91_emac_write(AT91_EMAC_CFG, AT91_EMAC_CLK_DIV32 | AT91_EMAC_BIG); + + /* Perform PHY-specific initialization */ + spin_lock_irq(&lp->lock); + enable_mdi(); + if ((phy_type == MII_DM9161_ID) || (lp->phy_type == MII_DM9161A_ID)) { + read_phy(phy_address, MII_DSCR_REG, &val); + if ((val & (1 << 10)) == 0) /* DSCR bit 10 is 0 -- fiber mode */ + lp->phy_media = PORT_FIBRE; + } else if (machine_is_csb337()) { + /* mix link activity status into LED2 link state */ + write_phy(phy_address, MII_LEDCTRL_REG, 0x0d22); + } + disable_mdi(); + spin_unlock_irq(&lp->lock); + + lp->mii.dev = dev; /* Support for ethtool */ + lp->mii.mdio_read = mdio_read; + lp->mii.mdio_write = mdio_write; + + lp->phy_type = phy_type; /* Type of PHY connected */ + lp->phy_address = phy_address; /* MDI address of PHY */ + + /* Register the network interface */ + res = register_netdev(dev); + if (res) { + free_irq(dev->irq, dev); + free_netdev(dev); + dma_free_coherent(NULL, sizeof(struct recv_desc_bufs), lp->dlist, (dma_addr_t)lp->dlist_phys); + return res; + } + at91_dev = dev; + + /* Determine current link speed */ + spin_lock_irq(&lp->lock); + enable_mdi(); + update_linkspeed(dev); + disable_mdi(); + spin_unlock_irq(&lp->lock); + netif_carrier_off(dev); /* will be enabled in open() */ + + /* Display ethernet banner */ + printk(KERN_INFO "%s: AT91 ethernet at 0x%08x int=%d %s%s (%02x:%02x:%02x:%02x:%02x:%02x)\n", + dev->name, (uint) dev->base_addr, dev->irq, + at91_emac_read(AT91_EMAC_CFG) & AT91_EMAC_SPD ? "100-" : "10-", + at91_emac_read(AT91_EMAC_CFG) & AT91_EMAC_FD ? "FullDuplex" : "HalfDuplex", + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + if ((phy_type == MII_DM9161_ID) || (lp->phy_type == MII_DM9161A_ID)) + printk(KERN_INFO "%s: Davicom 9196 PHY %s\n", dev->name, (lp->phy_media == PORT_FIBRE) ? "(Fiber)" : "(Copper)"); + else if (phy_type == MII_LXT971A_ID) + printk(KERN_INFO "%s: Intel LXT971A PHY\n", dev->name); + else if (phy_type == MII_RTL8201_ID) + printk(KERN_INFO "%s: Realtek RTL8201(B)L PHY\n", dev->name); + else if (phy_type == MII_BCM5221_ID) + printk(KERN_INFO "%s: Broadcom BCM5221 PHY\n", dev->name); + else if (phy_type == MII_DP83847_ID) + printk(KERN_INFO "%s: National Semiconductor DP83847 PHY\n", dev->name); + else if (phy_type == MII_AC101L_ID) + printk(KERN_INFO "%s: Altima AC101L PHY\n", dev->name); + else if (phy_type == MII_KS8721_ID) + printk(KERN_INFO "%s: Micrel KS8721 PHY\n", dev->name); + + return 0; +} + +/* + * Detect MAC and PHY and perform initialization + */ +static int __init at91ether_probe(struct platform_device *pdev) +{ + unsigned int phyid1, phyid2; + int detected = -1; + unsigned long phy_id; + unsigned short phy_address = 0; + + ether_clk = clk_get(&pdev->dev, "ether_clk"); + if (!ether_clk) { + printk(KERN_ERR "at91_ether: no clock defined\n"); + return -ENODEV; + } + clk_enable(ether_clk); /* Enable Peripheral clock */ + + while ((detected != 0) && (phy_address < 32)) { + /* Read the PHY ID registers */ + enable_mdi(); + read_phy(phy_address, MII_PHYSID1, &phyid1); + read_phy(phy_address, MII_PHYSID2, &phyid2); + disable_mdi(); + + phy_id = (phyid1 << 16) | (phyid2 & 0xfff0); + switch (phy_id) { + case MII_DM9161_ID: /* Davicom 9161: PHY_ID1 = 0x181, PHY_ID2 = B881 */ + case MII_DM9161A_ID: /* Davicom 9161A: PHY_ID1 = 0x181, PHY_ID2 = B8A0 */ + case MII_LXT971A_ID: /* Intel LXT971A: PHY_ID1 = 0x13, PHY_ID2 = 78E0 */ + case MII_RTL8201_ID: /* Realtek RTL8201: PHY_ID1 = 0, PHY_ID2 = 0x8201 */ + case MII_BCM5221_ID: /* Broadcom BCM5221: PHY_ID1 = 0x40, PHY_ID2 = 0x61e0 */ + case MII_DP83847_ID: /* National Semiconductor DP83847: */ + case MII_AC101L_ID: /* Altima AC101L: PHY_ID1 = 0x22, PHY_ID2 = 0x5520 */ + case MII_KS8721_ID: /* Micrel KS8721: PHY_ID1 = 0x22, PHY_ID2 = 0x1610 */ + detected = at91ether_setup(phy_id, phy_address, pdev); + break; + } + + phy_address++; + } + + clk_disable(ether_clk); /* Disable Peripheral clock */ + + return detected; +} + +static int __devexit at91ether_remove(struct platform_device *pdev) +{ + struct at91_private *lp = (struct at91_private *) at91_dev->priv; + + unregister_netdev(at91_dev); + free_irq(at91_dev->irq, at91_dev); + dma_free_coherent(NULL, sizeof(struct recv_desc_bufs), lp->dlist, (dma_addr_t)lp->dlist_phys); + clk_put(ether_clk); + + free_netdev(at91_dev); + at91_dev = NULL; + return 0; +} + +static struct platform_driver at91ether_driver = { + .probe = at91ether_probe, + .remove = __devexit_p(at91ether_remove), + /* FIXME: support suspend and resume */ + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init at91ether_init(void) +{ + return platform_driver_register(&at91ether_driver); +} + +static void __exit at91ether_exit(void) +{ + platform_driver_unregister(&at91ether_driver); +} + +module_init(at91ether_init) +module_exit(at91ether_exit) + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AT91RM9200 EMAC Ethernet driver"); +MODULE_AUTHOR("Andrew Victor"); diff --git a/drivers/net/arm/at91_ether.h b/drivers/net/arm/at91_ether.h new file mode 100644 index 000000000000..9885735c9c8a --- /dev/null +++ b/drivers/net/arm/at91_ether.h @@ -0,0 +1,101 @@ +/* + * Ethernet driver for the Atmel AT91RM9200 (Thunder) + * + * Copyright (C) SAN People (Pty) Ltd + * + * Based on an earlier Atmel EMAC macrocell driver by Atmel and Lineo Inc. + * Initial version by Rick Bronson. + * + * 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. + */ + +#ifndef AT91_ETHERNET +#define AT91_ETHERNET + + +/* Davicom 9161 PHY */ +#define MII_DM9161_ID 0x0181b880 +#define MII_DM9161A_ID 0x0181b8a0 + +/* Davicom specific registers */ +#define MII_DSCR_REG 16 +#define MII_DSCSR_REG 17 +#define MII_DSINTR_REG 21 + +/* Intel LXT971A PHY */ +#define MII_LXT971A_ID 0x001378E0 + +/* Intel specific registers */ +#define MII_ISINTE_REG 18 +#define MII_ISINTS_REG 19 +#define MII_LEDCTRL_REG 20 + +/* Realtek RTL8201 PHY */ +#define MII_RTL8201_ID 0x00008200 + +/* Broadcom BCM5221 PHY */ +#define MII_BCM5221_ID 0x004061e0 + +/* Broadcom specific registers */ +#define MII_BCMINTR_REG 26 + +/* National Semiconductor DP83847 */ +#define MII_DP83847_ID 0x20005c30 + +/* Altima AC101L PHY */ +#define MII_AC101L_ID 0x00225520 + +/* Micrel KS8721 PHY */ +#define MII_KS8721_ID 0x00221610 + +/* ........................................................................ */ + +#define MAX_RBUFF_SZ 0x600 /* 1518 rounded up */ +#define MAX_RX_DESCR 9 /* max number of receive buffers */ + +#define EMAC_DESC_DONE 0x00000001 /* bit for if DMA is done */ +#define EMAC_DESC_WRAP 0x00000002 /* bit for wrap */ + +#define EMAC_BROADCAST 0x80000000 /* broadcast address */ +#define EMAC_MULTICAST 0x40000000 /* multicast address */ +#define EMAC_UNICAST 0x20000000 /* unicast address */ + +struct rbf_t +{ + unsigned int addr; + unsigned long size; +}; + +struct recv_desc_bufs +{ + struct rbf_t descriptors[MAX_RX_DESCR]; /* must be on sizeof (rbf_t) boundary */ + char recv_buf[MAX_RX_DESCR][MAX_RBUFF_SZ]; /* must be on long boundary */ +}; + +struct at91_private +{ + struct net_device_stats stats; + struct mii_if_info mii; /* ethtool support */ + struct at91_eth_data board_data; /* board-specific configuration */ + + /* PHY */ + unsigned long phy_type; /* type of PHY (PHY_ID) */ + spinlock_t lock; /* lock for MDI interface */ + short phy_media; /* media interface type */ + unsigned short phy_address; /* 5-bit MDI address of PHY (0..31) */ + + /* Transmit */ + struct sk_buff *skb; /* holds skb until xmit interrupt completes */ + dma_addr_t skb_physaddr; /* phys addr from pci_map_single */ + int skb_length; /* saved skb length for pci_unmap_single */ + + /* Receive */ + int rxBuffIndex; /* index into receive descriptor list */ + struct recv_desc_bufs *dlist; /* descriptor list address */ + struct recv_desc_bufs *dlist_phys; /* descriptor list physical address */ +}; + +#endif diff --git a/include/asm-arm/arch-at91rm9200/at91rm9200_emac.h b/include/asm-arm/arch-at91rm9200/at91rm9200_emac.h new file mode 100644 index 000000000000..fbc091e61e2f --- /dev/null +++ b/include/asm-arm/arch-at91rm9200/at91rm9200_emac.h @@ -0,0 +1,138 @@ +/* + * include/asm-arm/arch-at91rm9200/at91rm9200_emac.h + * + * Copyright (C) 2005 Ivan Kokshaysky + * Copyright (C) SAN People + * + * Ethernet MAC registers. + * Based on AT91RM9200 datasheet revision E. + * + * 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. + */ + +#ifndef AT91RM9200_EMAC_H +#define AT91RM9200_EMAC_H + +#define AT91_EMAC_CTL 0x00 /* Control Register */ +#define AT91_EMAC_LB (1 << 0) /* Loopback */ +#define AT91_EMAC_LBL (1 << 1) /* Loopback Local */ +#define AT91_EMAC_RE (1 << 2) /* Receive Enable */ +#define AT91_EMAC_TE (1 << 3) /* Transmit Enable */ +#define AT91_EMAC_MPE (1 << 4) /* Management Port Enable */ +#define AT91_EMAC_CSR (1 << 5) /* Clear Statistics Registers */ +#define AT91_EMAC_INCSTAT (1 << 6) /* Increment Statistics Registers */ +#define AT91_EMAC_WES (1 << 7) /* Write Enable for Statistics Registers */ +#define AT91_EMAC_BP (1 << 8) /* Back Pressure */ + +#define AT91_EMAC_CFG 0x04 /* Configuration Register */ +#define AT91_EMAC_SPD (1 << 0) /* Speed */ +#define AT91_EMAC_FD (1 << 1) /* Full Duplex */ +#define AT91_EMAC_BR (1 << 2) /* Bit Rate */ +#define AT91_EMAC_CAF (1 << 4) /* Copy All Frames */ +#define AT91_EMAC_NBC (1 << 5) /* No Broadcast */ +#define AT91_EMAC_MTI (1 << 6) /* Multicast Hash Enable */ +#define AT91_EMAC_UNI (1 << 7) /* Unicast Hash Enable */ +#define AT91_EMAC_BIG (1 << 8) /* Receive 1522 Bytes */ +#define AT91_EMAC_EAE (1 << 9) /* External Address Match Enable */ +#define AT91_EMAC_CLK (3 << 10) /* MDC Clock Divisor */ +#define AT91_EMAC_CLK_DIV8 (0 << 10) +#define AT91_EMAC_CLK_DIV16 (1 << 10) +#define AT91_EMAC_CLK_DIV32 (2 << 10) +#define AT91_EMAC_CLK_DIV64 (3 << 10) +#define AT91_EMAC_RTY (1 << 12) /* Retry Test */ +#define AT91_EMAC_RMII (1 << 13) /* Reduce MII (RMII) */ + +#define AT91_EMAC_SR 0x08 /* Status Register */ +#define AT91_EMAC_SR_LINK (1 << 0) /* Link */ +#define AT91_EMAC_SR_MDIO (1 << 1) /* MDIO pin */ +#define AT91_EMAC_SR_IDLE (1 << 2) /* PHY idle */ + +#define AT91_EMAC_TAR 0x0c /* Transmit Address Register */ + +#define AT91_EMAC_TCR 0x10 /* Transmit Control Register */ +#define AT91_EMAC_LEN (0x7ff << 0) /* Transmit Frame Length */ +#define AT91_EMAC_NCRC (1 << 15) /* No CRC */ + +#define AT91_EMAC_TSR 0x14 /* Transmit Status Register */ +#define AT91_EMAC_TSR_OVR (1 << 0) /* Transmit Buffer Overrun */ +#define AT91_EMAC_TSR_COL (1 << 1) /* Collision Occurred */ +#define AT91_EMAC_TSR_RLE (1 << 2) /* Retry Limit Exceeded */ +#define AT91_EMAC_TSR_IDLE (1 << 3) /* Transmitter Idle */ +#define AT91_EMAC_TSR_BNQ (1 << 4) /* Transmit Buffer not Queued */ +#define AT91_EMAC_TSR_COMP (1 << 5) /* Transmit Complete */ +#define AT91_EMAC_TSR_UND (1 << 6) /* Transmit Underrun */ + +#define AT91_EMAC_RBQP 0x18 /* Receive Buffer Queue Pointer */ + +#define AT91_EMAC_RSR 0x20 /* Receive Status Register */ +#define AT91_EMAC_RSR_BNA (1 << 0) /* Buffer Not Available */ +#define AT91_EMAC_RSR_REC (1 << 1) /* Frame Received */ +#define AT91_EMAC_RSR_OVR (1 << 2) /* RX Overrun */ + +#define AT91_EMAC_ISR 0x24 /* Interrupt Status Register */ +#define AT91_EMAC_DONE (1 << 0) /* Management Done */ +#define AT91_EMAC_RCOM (1 << 1) /* Receive Complete */ +#define AT91_EMAC_RBNA (1 << 2) /* Receive Buffer Not Available */ +#define AT91_EMAC_TOVR (1 << 3) /* Transmit Buffer Overrun */ +#define AT91_EMAC_TUND (1 << 4) /* Transmit Buffer Underrun */ +#define AT91_EMAC_RTRY (1 << 5) /* Retry Limit */ +#define AT91_EMAC_TBRE (1 << 6) /* Transmit Buffer Register Empty */ +#define AT91_EMAC_TCOM (1 << 7) /* Transmit Complete */ +#define AT91_EMAC_TIDLE (1 << 8) /* Transmit Idle */ +#define AT91_EMAC_LINK (1 << 9) /* Link */ +#define AT91_EMAC_ROVR (1 << 10) /* RX Overrun */ +#define AT91_EMAC_ABT (1 << 11) /* Abort */ + +#define AT91_EMAC_IER 0x28 /* Interrupt Enable Register */ +#define AT91_EMAC_IDR 0x2c /* Interrupt Disable Register */ +#define AT91_EMAC_IMR 0x30 /* Interrupt Mask Register */ + +#define AT91_EMAC_MAN 0x34 /* PHY Maintenance Register */ +#define AT91_EMAC_DATA (0xffff << 0) /* MDIO Data */ +#define AT91_EMAC_REGA (0x1f << 18) /* MDIO Register */ +#define AT91_EMAC_PHYA (0x1f << 23) /* MDIO PHY Address */ +#define AT91_EMAC_RW (3 << 28) /* Read/Write operation */ +#define AT91_EMAC_RW_W (1 << 28) +#define AT91_EMAC_RW_R (2 << 28) +#define AT91_EMAC_MAN_802_3 0x40020000 /* IEEE 802.3 value */ + +/* + * Statistics Registers. + */ +#define AT91_EMAC_FRA 0x40 /* Frames Transmitted OK */ +#define AT91_EMAC_SCOL 0x44 /* Single Collision Frame */ +#define AT91_EMAC_MCOL 0x48 /* Multiple Collision Frame */ +#define AT91_EMAC_OK 0x4c /* Frames Received OK */ +#define AT91_EMAC_SEQE 0x50 /* Frame Check Sequence Error */ +#define AT91_EMAC_ALE 0x54 /* Alignmemt Error */ +#define AT91_EMAC_DTE 0x58 /* Deffered Transmission Frame */ +#define AT91_EMAC_LCOL 0x5c /* Late Collision */ +#define AT91_EMAC_ECOL 0x60 /* Excessive Collision */ +#define AT91_EMAC_TUE 0x64 /* Transmit Underrun Error */ +#define AT91_EMAC_CSE 0x68 /* Carrier Sense Error */ +#define AT91_EMAC_DRFC 0x6c /* Discard RX Frame */ +#define AT91_EMAC_ROV 0x70 /* Receive Overrun */ +#define AT91_EMAC_CDE 0x74 /* Code Error */ +#define AT91_EMAC_ELR 0x78 /* Excessive Length Error */ +#define AT91_EMAC_RJB 0x7c /* Receive Jabber */ +#define AT91_EMAC_USF 0x80 /* Undersize Frame */ +#define AT91_EMAC_SQEE 0x84 /* SQE Test Error */ + +/* + * Address Registers. + */ +#define AT91_EMAC_HSL 0x90 /* Hash Address Low [31:0] */ +#define AT91_EMAC_HSH 0x94 /* Hash Address High [63:32] */ +#define AT91_EMAC_SA1L 0x98 /* Specific Address 1 Low, bytes 0-3 */ +#define AT91_EMAC_SA1H 0x9c /* Specific Address 1 High, bytes 4-5 */ +#define AT91_EMAC_SA2L 0xa0 /* Specific Address 2 Low, bytes 0-3 */ +#define AT91_EMAC_SA2H 0xa4 /* Specific Address 2 High, bytes 4-5 */ +#define AT91_EMAC_SA3L 0xa8 /* Specific Address 3 Low, bytes 0-3 */ +#define AT91_EMAC_SA3H 0xac /* Specific Address 3 High, bytes 4-5 */ +#define AT91_EMAC_SA4L 0xb0 /* Specific Address 4 Low, bytes 0-3 */ +#define AT91_EMAC_SA4H 0xb4 /* Specific Address 4 High, bytes 4-5 */ + +#endif -- cgit v1.2.3 From 13fce8062968996da496d4f65cc1c1f845704604 Mon Sep 17 00:00:00 2001 From: Andrzej Zaborowski Date: Fri, 24 Mar 2006 18:13:37 +0100 Subject: Fix simple typos This corrects some trivial errors in ARM docs and comments, Signed-off-by: Adrian Bunk --- Documentation/arm/Booting | 2 +- Documentation/arm/README | 2 +- Documentation/arm/Setup | 2 +- include/linux/timer.h | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/arm/Booting b/Documentation/arm/Booting index fad566bb02fc..76850295af8f 100644 --- a/Documentation/arm/Booting +++ b/Documentation/arm/Booting @@ -118,7 +118,7 @@ to store page tables. The recommended placement is 32KiB into RAM. In either case, the following conditions must be met: -- Quiesce all DMA capable devicess so that memory does not get +- Quiesce all DMA capable devices so that memory does not get corrupted by bogus network packets or disk data. This will save you many hours of debug. diff --git a/Documentation/arm/README b/Documentation/arm/README index 5ed6f3530b86..9b9c8226fdc4 100644 --- a/Documentation/arm/README +++ b/Documentation/arm/README @@ -89,7 +89,7 @@ Modules Although modularisation is supported (and required for the FP emulator), each module on an ARM2/ARM250/ARM3 machine when is loaded will take memory up to the next 32k boundary due to the size of the pages. - Therefore, modularisation on these machines really worth it? + Therefore, is modularisation on these machines really worth it? However, ARM6 and up machines allow modules to take multiples of 4k, and as such Acorn RiscPCs and other architectures using these processors can diff --git a/Documentation/arm/Setup b/Documentation/arm/Setup index 0abd0720d7ed..0cb1e64bde80 100644 --- a/Documentation/arm/Setup +++ b/Documentation/arm/Setup @@ -58,7 +58,7 @@ below: video_y This describes the character position of cursor on VGA console, and - is otherwise unused. (should not used for other console types, and + is otherwise unused. (should not be used for other console types, and should not be used for other purposes). memc_control_reg diff --git a/include/linux/timer.h b/include/linux/timer.h index 9b9877fd2505..ee5a09e806e8 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -69,13 +69,13 @@ extern unsigned long next_timer_interrupt(void); * @timer: the timer to be added * * The kernel will do a ->function(->data) callback from the - * timer interrupt at the ->expired point in the future. The + * timer interrupt at the ->expires point in the future. The * current time is 'jiffies'. * - * The timer's ->expired, ->function (and if the handler uses it, ->data) + * The timer's ->expires, ->function (and if the handler uses it, ->data) * fields must be set prior calling this function. * - * Timers with an ->expired field in the past will be executed in the next + * Timers with an ->expires field in the past will be executed in the next * timer tick. */ static inline void add_timer(struct timer_list *timer) -- cgit v1.2.3 From d2a28ad9fa7bf16761d070d8a3338375e1574b32 Mon Sep 17 00:00:00 2001 From: Russ Anderson Date: Fri, 24 Mar 2006 09:49:52 -0800 Subject: [IA64] MCA recovery: kernel context recovery table Memory errors encountered by user applications may surface when the CPU is running in kernel context. The current code will not attempt recovery if the MCA surfaces in kernel context (privilage mode 0). This patch adds a check for cases where the user initiated the load that surfaces in kernel interrupt code. An example is a user process lauching a load from memory and the data in memory had bad ECC. Before the bad data gets to the CPU register, and interrupt comes in. The code jumps to the IVT interrupt entry point and begins execution in kernel context. The process of saving the user registers (SAVE_REST) causes the bad data to be loaded into a CPU register, triggering the MCA. The MCA surfaces in kernel context, even though the load was initiated from user context. As suggested by David and Tony, this patch uses an exception table like approach, puting the tagged recovery addresses in a searchable table. One difference from the exception table is that MCAs do not surface in precise places (such as with a TLB miss), so instead of tagging specific instructions, address ranges are registers. A single macro is used to do the tagging, with the input parameter being the label of the starting address and the macro being the ending address. This limits clutter in the code. This patch only tags one spot, the interrupt ivt entry. Testing showed that spot to be a "heavy hitter" with MCAs surfacing while saving user registers. Other spots can be added as needed by adding a single macro. Signed-off-by: Russ Anderson (rja@sgi.com) Signed-off-by: Tony Luck --- arch/ia64/kernel/ivt.S | 1 + arch/ia64/kernel/mca.c | 98 +++++++++++++++++++++++++++++++----------- arch/ia64/kernel/mca_drv.c | 22 +++++++--- arch/ia64/kernel/mca_drv.h | 7 +++ arch/ia64/kernel/mca_drv_asm.S | 13 ++---- arch/ia64/kernel/vmlinux.lds.S | 9 ++++ include/asm-ia64/asmmacro.h | 11 +++++ 7 files changed, 120 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S index dcd906fe5749..829a43cab797 100644 --- a/arch/ia64/kernel/ivt.S +++ b/arch/ia64/kernel/ivt.S @@ -865,6 +865,7 @@ ENTRY(interrupt) ;; SAVE_REST ;; + MCA_RECOVER_RANGE(interrupt) alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group mov out0=cr.ivr // pass cr.ivr as first arg add out1=16,sp // pass pointer to pt_regs as second arg diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index cedcae713e9f..87ff7fe33cfb 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -83,6 +83,7 @@ #include #include +#include "mca_drv.h" #include "entry.h" #if defined(IA64_MCA_DEBUG_INFO) @@ -281,6 +282,50 @@ ia64_mca_log_sal_error_record(int sal_info_type) ia64_sal_clear_state_info(sal_info_type); } +/* + * search_mca_table + * See if the MCA surfaced in an instruction range + * that has been tagged as recoverable. + * + * Inputs + * first First address range to check + * last Last address range to check + * ip Instruction pointer, address we are looking for + * + * Return value: + * 1 on Success (in the table)/ 0 on Failure (not in the table) + */ +int +search_mca_table (const struct mca_table_entry *first, + const struct mca_table_entry *last, + unsigned long ip) +{ + const struct mca_table_entry *curr; + u64 curr_start, curr_end; + + curr = first; + while (curr <= last) { + curr_start = (u64) &curr->start_addr + curr->start_addr; + curr_end = (u64) &curr->end_addr + curr->end_addr; + + if ((ip >= curr_start) && (ip <= curr_end)) { + return 1; + } + curr++; + } + return 0; +} + +/* Given an address, look for it in the mca tables. */ +int mca_recover_range(unsigned long addr) +{ + extern struct mca_table_entry __start___mca_table[]; + extern struct mca_table_entry __stop___mca_table[]; + + return search_mca_table(__start___mca_table, __stop___mca_table-1, addr); +} +EXPORT_SYMBOL_GPL(mca_recover_range); + #ifdef CONFIG_ACPI int cpe_vector = -1; @@ -747,31 +792,34 @@ ia64_mca_modify_original_stack(struct pt_regs *regs, ia64_mca_modify_comm(previous_current); goto no_mod; } - if (r13 != sos->prev_IA64_KR_CURRENT) { - msg = "inconsistent previous current and r13"; - goto no_mod; - } - if ((r12 - r13) >= KERNEL_STACK_SIZE) { - msg = "inconsistent r12 and r13"; - goto no_mod; - } - if ((ar_bspstore - r13) >= KERNEL_STACK_SIZE) { - msg = "inconsistent ar.bspstore and r13"; - goto no_mod; - } - va.p = old_bspstore; - if (va.f.reg < 5) { - msg = "old_bspstore is in the wrong region"; - goto no_mod; - } - if ((ar_bsp - r13) >= KERNEL_STACK_SIZE) { - msg = "inconsistent ar.bsp and r13"; - goto no_mod; - } - size += (ia64_rse_skip_regs(old_bspstore, slots) - old_bspstore) * 8; - if (ar_bspstore + size > r12) { - msg = "no room for blocked state"; - goto no_mod; + + if (!mca_recover_range(ms->pmsa_iip)) { + if (r13 != sos->prev_IA64_KR_CURRENT) { + msg = "inconsistent previous current and r13"; + goto no_mod; + } + if ((r12 - r13) >= KERNEL_STACK_SIZE) { + msg = "inconsistent r12 and r13"; + goto no_mod; + } + if ((ar_bspstore - r13) >= KERNEL_STACK_SIZE) { + msg = "inconsistent ar.bspstore and r13"; + goto no_mod; + } + va.p = old_bspstore; + if (va.f.reg < 5) { + msg = "old_bspstore is in the wrong region"; + goto no_mod; + } + if ((ar_bsp - r13) >= KERNEL_STACK_SIZE) { + msg = "inconsistent ar.bsp and r13"; + goto no_mod; + } + size += (ia64_rse_skip_regs(old_bspstore, slots) - old_bspstore) * 8; + if (ar_bspstore + size > r12) { + msg = "no room for blocked state"; + goto no_mod; + } } ia64_mca_modify_comm(previous_current); diff --git a/arch/ia64/kernel/mca_drv.c b/arch/ia64/kernel/mca_drv.c index e883d85906db..37c88eb55873 100644 --- a/arch/ia64/kernel/mca_drv.c +++ b/arch/ia64/kernel/mca_drv.c @@ -6,6 +6,7 @@ * Copyright (C) Hidetoshi Seto (seto.hidetoshi@jp.fujitsu.com) * Copyright (C) 2005 Silicon Graphics, Inc * Copyright (C) 2005 Keith Owens + * Copyright (C) 2006 Russ Anderson */ #include #include @@ -121,11 +122,12 @@ mca_page_isolate(unsigned long paddr) */ void -mca_handler_bh(unsigned long paddr) +mca_handler_bh(unsigned long paddr, void *iip, unsigned long ipsr) { - printk(KERN_ERR - "OS_MCA: process [pid: %d](%s) encounters MCA (paddr=%lx)\n", - current->pid, current->comm, paddr); + printk(KERN_ERR "OS_MCA: process [cpu %d, pid: %d, uid: %d, " + "iip: %p, psr: 0x%lx,paddr: 0x%lx](%s) encounters MCA.\n", + raw_smp_processor_id(), current->pid, current->uid, + iip, ipsr, paddr, current->comm); spin_lock(&mca_bh_lock); switch (mca_page_isolate(paddr)) { @@ -442,21 +444,26 @@ recover_from_read_error(slidx_table_t *slidx, if (!peidx_bottom(peidx) || !(peidx_bottom(peidx)->valid.minstate)) return 0; psr1 =(struct ia64_psr *)&(peidx_minstate_area(peidx)->pmsa_ipsr); + psr2 =(struct ia64_psr *)&(peidx_minstate_area(peidx)->pmsa_xpsr); /* * Check the privilege level of interrupted context. * If it is user-mode, then terminate affected process. */ - if (psr1->cpl != 0) { + + pmsa = sos->pal_min_state; + if (psr1->cpl != 0 || + ((psr2->cpl != 0) && mca_recover_range(pmsa->pmsa_iip))) { smei = peidx_bus_check(peidx, 0); if (smei->valid.target_identifier) { /* * setup for resume to bottom half of MCA, * "mca_handler_bhhook" */ - pmsa = sos->pal_min_state; - /* pass to bhhook as 1st argument (gr8) */ + /* pass to bhhook as argument (gr8, ...) */ pmsa->pmsa_gr[8-1] = smei->target_identifier; + pmsa->pmsa_gr[9-1] = pmsa->pmsa_iip; + pmsa->pmsa_gr[10-1] = pmsa->pmsa_ipsr; /* set interrupted return address (but no use) */ pmsa->pmsa_br0 = pmsa->pmsa_iip; /* change resume address to bottom half */ @@ -466,6 +473,7 @@ recover_from_read_error(slidx_table_t *slidx, psr2 = (struct ia64_psr *)&pmsa->pmsa_ipsr; psr2->cpl = 0; psr2->ri = 0; + psr2->bn = 1; psr2->i = 0; return 1; diff --git a/arch/ia64/kernel/mca_drv.h b/arch/ia64/kernel/mca_drv.h index e2f6fa1e0ef6..31a2e52bb16f 100644 --- a/arch/ia64/kernel/mca_drv.h +++ b/arch/ia64/kernel/mca_drv.h @@ -111,3 +111,10 @@ typedef struct slidx_table { slidx_foreach_entry(__pos, &((slidx)->sec)) { __count++; }\ __count; }) +struct mca_table_entry { + int start_addr; /* location-relative starting address of MCA recoverable range */ + int end_addr; /* location-relative ending address of MCA recoverable range */ +}; + +extern const struct mca_table_entry *search_mca_tables (unsigned long addr); +extern int mca_recover_range(unsigned long); diff --git a/arch/ia64/kernel/mca_drv_asm.S b/arch/ia64/kernel/mca_drv_asm.S index 3f298ee4d00c..e6a580d354b9 100644 --- a/arch/ia64/kernel/mca_drv_asm.S +++ b/arch/ia64/kernel/mca_drv_asm.S @@ -14,15 +14,12 @@ GLOBAL_ENTRY(mca_handler_bhhook) invala // clear RSE ? - ;; cover ;; clrrrb ;; - alloc r16=ar.pfs,0,2,1,0 // make a new frame - ;; + alloc r16=ar.pfs,0,2,3,0 // make a new frame mov ar.rsc=0 - ;; mov r13=IA64_KR(CURRENT) // current task pointer ;; mov r2=r13 @@ -30,7 +27,6 @@ GLOBAL_ENTRY(mca_handler_bhhook) addl r22=IA64_RBS_OFFSET,r2 ;; mov ar.bspstore=r22 - ;; addl sp=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r2 ;; adds r2=IA64_TASK_THREAD_ON_USTACK_OFFSET,r13 @@ -40,12 +36,12 @@ GLOBAL_ENTRY(mca_handler_bhhook) movl loc1=mca_handler_bh // recovery C function ;; mov out0=r8 // poisoned address + mov out1=r9 // iip + mov out2=r10 // psr mov b6=loc1 ;; mov loc1=rp - ;; - ssm psr.i - ;; + ssm psr.i | psr.ic br.call.sptk.many rp=b6 // does not return ... ;; mov ar.pfs=loc0 @@ -53,5 +49,4 @@ GLOBAL_ENTRY(mca_handler_bhhook) ;; mov r8=r0 br.ret.sptk.many rp - ;; END(mca_handler_bhhook) diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S index 632d65cc0685..0b9e56dd7f05 100644 --- a/arch/ia64/kernel/vmlinux.lds.S +++ b/arch/ia64/kernel/vmlinux.lds.S @@ -130,6 +130,15 @@ SECTIONS __initcall_end = .; } + /* MCA table */ + . = ALIGN(16); + __mca_table : AT(ADDR(__mca_table) - LOAD_OFFSET) + { + __start___mca_table = .; + *(__mca_table) + __stop___mca_table = .; + } + .data.patch.vtop : AT(ADDR(.data.patch.vtop) - LOAD_OFFSET) { __start___vtop_patchlist = .; diff --git a/include/asm-ia64/asmmacro.h b/include/asm-ia64/asmmacro.h index 77af457f4ad7..d4cec32083d8 100644 --- a/include/asm-ia64/asmmacro.h +++ b/include/asm-ia64/asmmacro.h @@ -50,6 +50,17 @@ name: .xdata4 "__ex_table", 99f-., y-.+4; \ [99:] x +/* + * Tag MCA recoverable instruction ranges. + */ + + .section "__mca_table", "a" // declare section & section attributes + .previous + +# define MCA_RECOVER_RANGE(y) \ + .xdata4 "__mca_table", y-., 99f-.; \ + [99:] + /* * Mark instructions that need a load of a virtual address patched to be * a load of a physical address. We use this either in critical performance -- cgit v1.2.3 From c7b0ac0546985fc6361a8d92cf808d46da797677 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 10 Mar 2006 12:29:15 -0300 Subject: V4L/DVB (3516): Make video_buf more generic Video_buf were concerned to allow PCI devices to be used as video capture devices. This patch extends video_buf features by virtualizing pci-dependent functions and allowing other type of devices to use it. It is still DMA centric, although it may be used also by devices that emulates scatter/gather behavior or a DMA device Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 5 +- drivers/media/common/saa7146_vbi.c | 8 +- drivers/media/common/saa7146_video.c | 8 +- drivers/media/video/bttv-driver.c | 19 ++- drivers/media/video/bttv-risc.c | 4 +- drivers/media/video/bttv-vbi.c | 6 +- drivers/media/video/bttvp.h | 3 +- drivers/media/video/cx88/cx88-alsa.c | 4 +- drivers/media/video/cx88/cx88-blackbird.c | 5 +- drivers/media/video/cx88/cx88-core.c | 6 +- drivers/media/video/cx88/cx88-dvb.c | 5 +- drivers/media/video/cx88/cx88-mpeg.c | 28 ++-- drivers/media/video/cx88/cx88-vbi.c | 7 +- drivers/media/video/cx88/cx88-video.c | 35 +++- drivers/media/video/cx88/cx88.h | 6 +- drivers/media/video/saa7134/saa7134-alsa.c | 10 +- drivers/media/video/saa7134/saa7134-core.c | 4 +- drivers/media/video/saa7134/saa7134-oss.c | 6 +- drivers/media/video/saa7134/saa7134-ts.c | 9 +- drivers/media/video/saa7134/saa7134-vbi.c | 10 +- drivers/media/video/saa7134/saa7134-video.c | 9 +- drivers/media/video/saa7134/saa7134.h | 2 +- drivers/media/video/v4l2-common.c | 6 +- drivers/media/video/video-buf.c | 250 +++++++++++++++++++++------- include/media/saa7146_vv.h | 3 +- include/media/video-buf.h | 56 +++++-- 26 files changed, 340 insertions(+), 174 deletions(-) (limited to 'include') diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 3870fa948cc0..523ab3851c7b 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -50,14 +50,15 @@ void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits) /********************************************************************************/ /* common dma functions */ -void saa7146_dma_free(struct saa7146_dev *dev,struct saa7146_buf *buf) +void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q, + struct saa7146_buf *buf) { DEB_EE(("dev:%p, buf:%p\n",dev,buf)); BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_pci_unmap(dev->pci, &buf->vb.dma); + videobuf_dma_unmap(q, &buf->vb.dma); videobuf_dma_free(&buf->vb.dma); buf->vb.state = STATE_NEEDS_INIT; } diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c index 500bd3f05e16..063608462ebe 100644 --- a/drivers/media/common/saa7146_vbi.c +++ b/drivers/media/common/saa7146_vbi.c @@ -236,7 +236,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e } if (buf->vb.size != size) - saa7146_dma_free(dev,buf); + saa7146_dma_free(dev,q,buf); if (STATE_NEEDS_INIT == buf->vb.state) { buf->vb.width = llength; @@ -247,7 +247,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e saa7146_pgtable_free(dev->pci, &buf->pt[2]); saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); - err = videobuf_iolock(dev->pci,&buf->vb, NULL); + err = videobuf_iolock(q,&buf->vb, NULL); if (err) goto oops; err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], buf->vb.dma.sglist, buf->vb.dma.sglen); @@ -261,7 +261,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e oops: DEB_VBI(("error out.\n")); - saa7146_dma_free(dev,buf); + saa7146_dma_free(dev,q,buf); return err; } @@ -301,7 +301,7 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_VBI(("vb:%p\n",vb)); - saa7146_dma_free(dev,buf); + saa7146_dma_free(dev,q,buf); } static struct videobuf_queue_ops vbi_qops = { diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 6b42713d97f4..e7079d1bd537 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -1275,7 +1275,7 @@ static int buffer_prepare(struct videobuf_queue *q, buf->vb.field != field || buf->vb.field != fh->video_fmt.field || buf->fmt != &fh->video_fmt) { - saa7146_dma_free(dev,buf); + saa7146_dma_free(dev,q,buf); } if (STATE_NEEDS_INIT == buf->vb.state) { @@ -1304,7 +1304,7 @@ static int buffer_prepare(struct videobuf_queue *q, saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); } - err = videobuf_iolock(dev->pci,&buf->vb, &vv->ov_fb); + err = videobuf_iolock(q,&buf->vb, &vv->ov_fb); if (err) goto oops; err = saa7146_pgtable_build(dev,buf); @@ -1318,7 +1318,7 @@ static int buffer_prepare(struct videobuf_queue *q, oops: DEB_D(("error out.\n")); - saa7146_dma_free(dev,buf); + saa7146_dma_free(dev,q,buf); return err; } @@ -1363,7 +1363,7 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_CAP(("vbuf:%p\n",vb)); - saa7146_dma_free(dev,buf); + saa7146_dma_free(dev,q,buf); } static struct videobuf_queue_ops video_qops = { diff --git a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c index c0415d6e7fee..2505ae5a7b97 100644 --- a/drivers/media/video/bttv-driver.c +++ b/drivers/media/video/bttv-driver.c @@ -1397,7 +1397,7 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, free_btres(btv,fh,RESOURCE_OVERLAY); if (NULL != old) { dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state); - bttv_dma_free(btv, old); + bttv_dma_free(&fh->cap,btv, old); kfree(old); } dprintk("switch_overlay: done\n"); @@ -1407,7 +1407,8 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, /* ----------------------------------------------------------------------- */ /* video4linux (1) interface */ -static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf, +static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, + struct bttv_buffer *buf, const struct bttv_format *fmt, unsigned int width, unsigned int height, enum v4l2_field field) @@ -1450,7 +1451,7 @@ static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf, /* alloc risc memory */ if (STATE_NEEDS_INIT == buf->vb.state) { redo_dma_risc = 1; - if (0 != (rc = videobuf_iolock(btv->c.pci,&buf->vb,&btv->fbuf))) + if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf))) goto fail; } @@ -1462,7 +1463,7 @@ static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf, return 0; fail: - bttv_dma_free(btv,buf); + bttv_dma_free(q,btv,buf); return rc; } @@ -1486,7 +1487,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); struct bttv_fh *fh = q->priv_data; - return bttv_prepare_buffer(fh->btv, buf, fh->fmt, + return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt, fh->width, fh->height, field); } @@ -1510,7 +1511,7 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); struct bttv_fh *fh = q->priv_data; - bttv_dma_free(fh->btv,buf); + bttv_dma_free(&fh->cap,fh->btv,buf); } static struct videobuf_queue_ops bttv_video_qops = { @@ -2496,7 +2497,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, field = (vm->height > bttv_tvnorms[btv->tvnorm].sheight/2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; - retval = bttv_prepare_buffer(btv,buf, + retval = bttv_prepare_buffer(&fh->cap,btv,buf, format_by_palette(vm->format), vm->width,vm->height,field); if (0 != retval) @@ -2528,8 +2529,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, retval = -EIO; /* fall through */ case STATE_DONE: - videobuf_dma_pci_sync(btv->c.pci,&buf->vb.dma); - bttv_dma_free(btv,buf); + videobuf_dma_sync(&fh->cap,&buf->vb.dma); + bttv_dma_free(&fh->cap,btv,buf); break; default: retval = -EINVAL; diff --git a/drivers/media/video/bttv-risc.c b/drivers/media/video/bttv-risc.c index 344f84e9af04..16323a5d68ac 100644 --- a/drivers/media/video/bttv-risc.c +++ b/drivers/media/video/bttv-risc.c @@ -509,11 +509,11 @@ bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc, } void -bttv_dma_free(struct bttv *btv, struct bttv_buffer *buf) +bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf) { BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_pci_unmap(btv->c.pci, &buf->vb.dma); + videobuf_dma_unmap(q, &buf->vb.dma); videobuf_dma_free(&buf->vb.dma); btcx_riscmem_free(btv->c.pci,&buf->bottom); btcx_riscmem_free(btv->c.pci,&buf->top); diff --git a/drivers/media/video/bttv-vbi.c b/drivers/media/video/bttv-vbi.c index 72afdd64b882..e20ff238e409 100644 --- a/drivers/media/video/bttv-vbi.c +++ b/drivers/media/video/bttv-vbi.c @@ -96,7 +96,7 @@ static int vbi_buffer_prepare(struct videobuf_queue *q, return -EINVAL; if (STATE_NEEDS_INIT == buf->vb.state) { - if (0 != (rc = videobuf_iolock(btv->c.pci, &buf->vb, NULL))) + if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL))) goto fail; if (0 != (rc = vbi_buffer_risc(btv,buf,fh->lines))) goto fail; @@ -109,7 +109,7 @@ static int vbi_buffer_prepare(struct videobuf_queue *q, return 0; fail: - bttv_dma_free(btv,buf); + bttv_dma_free(q,btv,buf); return rc; } @@ -136,7 +136,7 @@ static void vbi_buffer_release(struct videobuf_queue *q, struct videobuf_buffer struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); dprintk("free %p\n",vb); - bttv_dma_free(fh->btv,buf); + bttv_dma_free(&fh->cap,fh->btv,buf); } struct videobuf_queue_ops bttv_vbi_qops = { diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h index 9cb72f176f7d..12223a203960 100644 --- a/drivers/media/video/bttvp.h +++ b/drivers/media/video/bttvp.h @@ -190,7 +190,8 @@ int bttv_buffer_activate_video(struct bttv *btv, struct bttv_buffer_set *set); int bttv_buffer_activate_vbi(struct bttv *btv, struct bttv_buffer *vbi); -void bttv_dma_free(struct bttv *btv, struct bttv_buffer *buf); +void bttv_dma_free(struct videobuf_queue *q, struct bttv *btv, + struct bttv_buffer *buf); /* overlay handling */ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov, diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 3170b8f72c68..f9d87b86492c 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -303,7 +303,7 @@ static int dsp_buffer_free(snd_cx88_card_t *chip) BUG_ON(!chip->dma_size); dprintk(2,"Freeing buffer\n"); - videobuf_dma_pci_unmap(chip->pci, &chip->dma_risc); + videobuf_pci_dma_unmap(chip->pci, &chip->dma_risc); videobuf_dma_free(&chip->dma_risc); btcx_riscmem_free(chip->pci,&chip->buf->risc); kfree(chip->buf); @@ -429,7 +429,7 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, videobuf_dma_init_kernel(&buf->vb.dma,PCI_DMA_FROMDEVICE, (PAGE_ALIGN(buf->vb.size) >> PAGE_SHIFT)); - videobuf_dma_pci_map(chip->pci,&buf->vb.dma); + videobuf_pci_dma_map(chip->pci,&buf->vb.dma); cx88_risc_databuffer(chip->pci, &buf->risc, diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index a502a4d6e4ae..e100d8ef369a 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -1341,7 +1341,7 @@ bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { struct cx8802_fh *fh = q->priv_data; - return cx8802_buf_prepare(fh->dev, (struct cx88_buffer*)vb, field); + return cx8802_buf_prepare(q, fh->dev, (struct cx88_buffer*)vb, field); } static void @@ -1354,8 +1354,7 @@ bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) static void bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { - struct cx8802_fh *fh = q->priv_data; - cx88_free_buffer(fh->dev->pci, (struct cx88_buffer*)vb); + cx88_free_buffer(q, (struct cx88_buffer*)vb); } static struct videobuf_queue_ops blackbird_qops = { diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index c2cdbafdb77b..2c3d9f1999be 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -213,13 +213,13 @@ int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, } void -cx88_free_buffer(struct pci_dev *pci, struct cx88_buffer *buf) +cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf) { BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_pci_unmap(pci, &buf->vb.dma); + videobuf_dma_unmap(q, &buf->vb.dma); videobuf_dma_free(&buf->vb.dma); - btcx_riscmem_free(pci, &buf->risc); + btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc); buf->vb.state = STATE_NEEDS_INIT; } diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index a9fc2695b157..f0ea9b5cdbc2 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -90,7 +90,7 @@ static int dvb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { struct cx8802_dev *dev = q->priv_data; - return cx8802_buf_prepare(dev, (struct cx88_buffer*)vb,field); + return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field); } static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) @@ -101,8 +101,7 @@ static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) static void dvb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { - struct cx8802_dev *dev = q->priv_data; - cx88_free_buffer(dev->pci, (struct cx88_buffer*)vb); + cx88_free_buffer(q, (struct cx88_buffer*)vb); } static struct videobuf_queue_ops dvb_qops = { diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index c79cc1d2bf8b..7d16888b4a86 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -163,8 +163,8 @@ static int cx8802_restart_queue(struct cx8802_dev *dev, /* ------------------------------------------------------------------ */ -int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf, - enum v4l2_field field) +int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev, + struct cx88_buffer *buf, enum v4l2_field field) { int size = dev->ts_packet_size * dev->ts_packet_count; int rc; @@ -179,7 +179,7 @@ int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf, buf->vb.size = size; buf->vb.field = field /*V4L2_FIELD_TOP*/; - if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL))) + if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) goto fail; cx88_risc_databuffer(dev->pci, &buf->risc, buf->vb.dma.sglist, @@ -189,36 +189,36 @@ int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf, return 0; fail: - cx88_free_buffer(dev->pci,buf); + cx88_free_buffer(q,buf); return rc; } void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) { struct cx88_buffer *prev; - struct cx88_dmaqueue *q = &dev->mpegq; + struct cx88_dmaqueue *cx88q = &dev->mpegq; dprintk( 1, "cx8802_buf_queue\n" ); /* add jump to stopper */ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma); - if (list_empty(&q->active)) { + if (list_empty(&cx88q->active)) { dprintk( 0, "queue is empty - first active\n" ); - list_add_tail(&buf->vb.queue,&q->active); - cx8802_start_dma(dev, q, buf); + list_add_tail(&buf->vb.queue,&cx88q->active); + cx8802_start_dma(dev, cx88q, buf); buf->vb.state = STATE_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); + buf->count = cx88q->count++; + mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT); dprintk(0,"[%p/%d] %s - first active\n", buf, buf->vb.i, __FUNCTION__); } else { dprintk( 1, "queue is not empty - append to active\n" ); - prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); - list_add_tail(&buf->vb.queue,&q->active); + prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue); + list_add_tail(&buf->vb.queue,&cx88q->active); buf->vb.state = STATE_ACTIVE; - buf->count = q->count++; + buf->count = cx88q->count++; prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); dprintk( 1, "[%p/%d] %s - append to active\n", buf, buf->vb.i, __FUNCTION__); diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/video/cx88/cx88-vbi.c index 9bc6c8995581..846faadc9f1c 100644 --- a/drivers/media/video/cx88/cx88-vbi.c +++ b/drivers/media/video/cx88/cx88-vbi.c @@ -175,7 +175,7 @@ vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, buf->vb.size = size; buf->vb.field = V4L2_FIELD_SEQ_TB; - if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL))) + if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) goto fail; cx88_risc_buffer(dev->pci, &buf->risc, buf->vb.dma.sglist, @@ -187,7 +187,7 @@ vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, return 0; fail: - cx88_free_buffer(dev->pci,buf); + cx88_free_buffer(q,buf); return rc; } @@ -227,9 +227,8 @@ vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); - struct cx8800_fh *fh = q->priv_data; - cx88_free_buffer(fh->dev->pci,buf); + cx88_free_buffer(q,buf); } struct videobuf_queue_ops cx8800_vbi_qops = { diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 6c97aa740d27..72a417b31745 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -564,7 +564,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, if (STATE_NEEDS_INIT == buf->vb.state) { init_buffer = 1; - if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL))) + if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) goto fail; } @@ -614,7 +614,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, return 0; fail: - cx88_free_buffer(dev->pci,buf); + cx88_free_buffer(q,buf); return rc; } @@ -671,9 +671,8 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); - struct cx8800_fh *fh = q->priv_data; - cx88_free_buffer(fh->dev->pci,buf); + cx88_free_buffer(q,buf); } static struct videobuf_queue_ops cx8800_video_qops = { @@ -1251,9 +1250,17 @@ int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, { int err; - dprintk(2, "CORE IOCTL: 0x%x\n", cmd ); - if (video_debug > 1) - v4l_print_ioctl(core->name,cmd); + if (video_debug) { + if (video_debug > 1) { + if (_IOC_DIR(cmd) & _IOC_WRITE) + v4l_printk_ioctl_arg("cx88(w)",cmd, arg); + else if (!_IOC_DIR(cmd) & _IOC_READ) { + v4l_print_ioctl("cx88", cmd); + } + } else + v4l_print_ioctl(core->name,cmd); + + } switch (cmd) { /* ---------- tv norms ---------- */ @@ -1460,7 +1467,19 @@ int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, static int video_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(inode, file, cmd, arg, video_do_ioctl); + int retval; + + retval=video_usercopy(inode, file, cmd, arg, video_do_ioctl); + + if (video_debug > 1) { + if (retval < 0) { + v4l_print_ioctl("cx88(err)", cmd); + printk(KERN_DEBUG "cx88(err): errcode=%d\n",retval); + } else if (_IOC_DIR(cmd) & _IOC_READ) + v4l_printk_ioctl_arg("cx88(r)",cmd, (void *)arg); + } + + return retval; } /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index cfa8668784b4..5b2e499eab22 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -485,7 +485,7 @@ extern int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, u32 reg, u32 mask, u32 value); extern void -cx88_free_buffer(struct pci_dev *pci, struct cx88_buffer *buf); +cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf); extern void cx88_risc_disasm(struct cx88_core *core, struct btcx_riscmem *risc); @@ -577,8 +577,8 @@ void cx88_ir_irq(struct cx88_core *core); /* ----------------------------------------------------------- */ /* cx88-mpeg.c */ -int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf, - enum v4l2_field field); +int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev, + struct cx88_buffer *buf, enum v4l2_field field); void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf); void cx8802_cancel_buffers(struct cx8802_dev *dev); diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index aca84d2f9825..bb3e0ba946d3 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -507,7 +507,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, /* release the old buffer */ if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma); + videobuf_pci_dma_unmap(dev->pci, &dev->dmasound.dma); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } @@ -523,12 +523,12 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, return err; } - if (0 != (err = videobuf_dma_pci_map(dev->pci, &dev->dmasound.dma))) { + if (0 != (err = videobuf_pci_dma_map(dev->pci, &dev->dmasound.dma))) { dsp_buffer_free(dev); return err; } if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) { - videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma); + videobuf_pci_dma_unmap(dev->pci, &dev->dmasound.dma); dsp_buffer_free(dev); return err; } @@ -537,7 +537,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, dev->dmasound.dma.sglen, 0))) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma); + videobuf_pci_dma_unmap(dev->pci, &dev->dmasound.dma); dsp_buffer_free(dev); return err; } @@ -571,7 +571,7 @@ static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma); + videobuf_pci_dma_unmap(dev->pci, &dev->dmasound.dma); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 58e568d7d2ee..15405d1e1675 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -254,12 +254,12 @@ void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt) /* ------------------------------------------------------------------ */ -void saa7134_dma_free(struct saa7134_dev *dev,struct saa7134_buf *buf) +void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf) { BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_pci_unmap(dev->pci, &buf->vb.dma); + videobuf_dma_unmap(q, &buf->vb.dma); videobuf_dma_free(&buf->vb.dma); buf->vb.state = STATE_NEEDS_INIT; } diff --git a/drivers/media/video/saa7134/saa7134-oss.c b/drivers/media/video/saa7134/saa7134-oss.c index d79d05f88705..7aa02b34e012 100644 --- a/drivers/media/video/saa7134/saa7134-oss.c +++ b/drivers/media/video/saa7134/saa7134-oss.c @@ -124,7 +124,7 @@ static int dsp_rec_start(struct saa7134_dev *dev) unsigned long flags; /* prepare buffer */ - if (0 != (err = videobuf_dma_pci_map(dev->pci,&dev->dmasound.dma))) + if (0 != (err = videobuf_pci_dma_map(dev->pci,&dev->dmasound.dma))) return err; if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) goto fail1; @@ -213,7 +213,7 @@ static int dsp_rec_start(struct saa7134_dev *dev) fail2: saa7134_pgtable_free(dev->pci,&dev->dmasound.pt); fail1: - videobuf_dma_pci_unmap(dev->pci,&dev->dmasound.dma); + videobuf_pci_dma_unmap(dev->pci,&dev->dmasound.dma); return err; } @@ -231,7 +231,7 @@ static int dsp_rec_stop(struct saa7134_dev *dev) /* unlock buffer */ saa7134_pgtable_free(dev->pci,&dev->dmasound.pt); - videobuf_dma_pci_unmap(dev->pci,&dev->dmasound.dma); + videobuf_pci_dma_unmap(dev->pci,&dev->dmasound.dma); return 0; } diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c index 470903e2f5e5..60a90a2617ae 100644 --- a/drivers/media/video/saa7134/saa7134-ts.c +++ b/drivers/media/video/saa7134/saa7134-ts.c @@ -89,7 +89,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, return -EINVAL; if (buf->vb.size != size) { - saa7134_dma_free(dev,buf); + saa7134_dma_free(q,buf); } if (STATE_NEEDS_INIT == buf->vb.state) { @@ -98,7 +98,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, buf->vb.size = size; buf->pt = &dev->ts.pt_ts; - err = videobuf_iolock(dev->pci,&buf->vb,NULL); + err = videobuf_iolock(q,&buf->vb,NULL); if (err) goto oops; err = saa7134_pgtable_build(dev->pci,buf->pt, @@ -126,7 +126,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, return 0; oops: - saa7134_dma_free(dev,buf); + saa7134_dma_free(q,buf); return err; } @@ -152,10 +152,9 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { - struct saa7134_dev *dev = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); - saa7134_dma_free(dev,buf); + saa7134_dma_free(q,buf); } struct videobuf_queue_ops saa7134_ts_qops = { diff --git a/drivers/media/video/saa7134/saa7134-vbi.c b/drivers/media/video/saa7134/saa7134-vbi.c index f4aee0af80e1..f38366a470fa 100644 --- a/drivers/media/video/saa7134/saa7134-vbi.c +++ b/drivers/media/video/saa7134/saa7134-vbi.c @@ -135,7 +135,7 @@ static int buffer_prepare(struct videobuf_queue *q, return -EINVAL; if (buf->vb.size != size) - saa7134_dma_free(dev,buf); + saa7134_dma_free(q,buf); if (STATE_NEEDS_INIT == buf->vb.state) { buf->vb.width = llength; @@ -143,7 +143,7 @@ static int buffer_prepare(struct videobuf_queue *q, buf->vb.size = size; buf->pt = &fh->pt_vbi; - err = videobuf_iolock(dev->pci,&buf->vb,NULL); + err = videobuf_iolock(q,&buf->vb,NULL); if (err) goto oops; err = saa7134_pgtable_build(dev->pci,buf->pt, @@ -159,7 +159,7 @@ static int buffer_prepare(struct videobuf_queue *q, return 0; oops: - saa7134_dma_free(dev,buf); + saa7134_dma_free(q,buf); return err; } @@ -190,11 +190,9 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { - struct saa7134_fh *fh = q->priv_data; - struct saa7134_dev *dev = fh->dev; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); - saa7134_dma_free(dev,buf); + saa7134_dma_free(q,buf); } struct videobuf_queue_ops saa7134_vbi_qops = { diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 57a11e71d996..aeef80f88a6b 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -993,7 +993,7 @@ static int buffer_prepare(struct videobuf_queue *q, buf->vb.size != size || buf->vb.field != field || buf->fmt != fh->fmt) { - saa7134_dma_free(dev,buf); + saa7134_dma_free(q,buf); } if (STATE_NEEDS_INIT == buf->vb.state) { @@ -1004,7 +1004,7 @@ static int buffer_prepare(struct videobuf_queue *q, buf->fmt = fh->fmt; buf->pt = &fh->pt_cap; - err = videobuf_iolock(dev->pci,&buf->vb,&dev->ovbuf); + err = videobuf_iolock(q,&buf->vb,&dev->ovbuf); if (err) goto oops; err = saa7134_pgtable_build(dev->pci,buf->pt, @@ -1019,7 +1019,7 @@ static int buffer_prepare(struct videobuf_queue *q, return 0; oops: - saa7134_dma_free(dev,buf); + saa7134_dma_free(q,buf); return err; } @@ -1045,10 +1045,9 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { - struct saa7134_fh *fh = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); - saa7134_dma_free(fh->dev,buf); + saa7134_dma_free(q,buf); } static struct videobuf_queue_ops video_qops = { diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index ce1c2e0b065e..104bd2e054e5 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -579,7 +579,7 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, unsigned int state); void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q); void saa7134_buffer_timeout(unsigned long data); -void saa7134_dma_free(struct saa7134_dev *dev,struct saa7134_buf *buf); +void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf); int saa7134_set_dmabits(struct saa7134_dev *dev); diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 95a6e47c99f1..9b029e4dbfb7 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -481,7 +481,7 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) prt_names(p->memory,v4l2_memory_names), p->m.userptr); printk ("%s: timecode= %02d:%02d:%02d type=%d, " - "flags=0x%08d, frames=%d, userbits=0x%08x", + "flags=0x%08d, frames=%d, userbits=0x%08x\n", s,tc->hours,tc->minutes,tc->seconds, tc->type, tc->flags, tc->frames, (__u32) tc->userbits); break; @@ -489,8 +489,8 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) case VIDIOC_QUERYCAP: { struct v4l2_capability *p=arg; - printk ("%s: driver=%s, card=%s, bus=%s, version=%d, " - "capabilities=%d\n", s, + printk ("%s: driver=%s, card=%s, bus=%s, version=0x%08x, " + "capabilities=0x%08x\n", s, p->driver,p->card,p->bus_info, p->version, p->capabilities); diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index 87e937581d5a..d2ca0f08d0df 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -1,15 +1,20 @@ /* * * generic helper functions for video4linux capture buffers, to handle - * memory management and PCI DMA. Right now bttv + saa7134 use it. + * memory management and PCI DMA. + * Right now, bttv, saa7134, saa7146 and cx88 use it. * * The functions expect the hardware being able to scatter gatter * (i.e. the buffers are not linear in physical memory, but fragmented * into PAGE_SIZE chunks). They also assume the driver does not need - * to touch the video data (thus it is probably not useful for USB 1.1 - * as data often must be uncompressed by the drivers). + * to touch the video data. + * + * device specific map/unmap/sync stuff now are mapped as operations + * to allow its usage by USB and virtual devices. * * (c) 2001-2004 Gerd Knorr [SUSE Labs] + * (c) 2006 Mauro Carvalho Chehab + * (c) 2006 Ted Walther and John Sokol * * 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 @@ -167,6 +172,9 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, dprintk(1,"vmalloc_32(%d pages) failed\n",nr_pages); return -ENOMEM; } + dprintk(1,"vmalloc is at addr 0x%08lx, size=%d\n", + (unsigned long)dma->vmalloc, + nr_pages << PAGE_SHIFT); memset(dma->vmalloc,0,nr_pages << PAGE_SHIFT); dma->nr_pages = nr_pages; return 0; @@ -186,8 +194,10 @@ int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, return 0; } -int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma) +int videobuf_dma_map(struct videobuf_queue* q,struct videobuf_dmabuf *dma) { + void *dev=q->dev; + MAGIC_CHECK(dma->magic,MAGIC_DMABUF); BUG_ON(0 == dma->nr_pages); @@ -197,7 +207,7 @@ int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma) } if (dma->vmalloc) { dma->sglist = videobuf_vmalloc_to_sg - (dma->vmalloc,dma->nr_pages); + (dma->vmalloc,dma->nr_pages); } if (dma->bus_addr) { dma->sglist = kmalloc(sizeof(struct scatterlist), GFP_KERNEL); @@ -212,13 +222,14 @@ int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma) dprintk(1,"scatterlist is NULL\n"); return -ENOMEM; } - if (!dma->bus_addr) { - dma->sglen = pci_map_sg(dev,dma->sglist,dma->nr_pages, - dma->direction); + if (q->ops->vb_map_sg) { + dma->sglen = q->ops->vb_map_sg(dev,dma->sglist, + dma->nr_pages, dma->direction); + } if (0 == dma->sglen) { printk(KERN_WARNING - "%s: pci_map_sg failed\n",__FUNCTION__); + "%s: videobuf_map_sg failed\n",__FUNCTION__); kfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; @@ -228,24 +239,31 @@ int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma) return 0; } -int videobuf_dma_pci_sync(struct pci_dev *dev, struct videobuf_dmabuf *dma) +int videobuf_dma_sync(struct videobuf_queue* q,struct videobuf_dmabuf *dma) { + void *dev=q->dev; + MAGIC_CHECK(dma->magic,MAGIC_DMABUF); BUG_ON(!dma->sglen); - if (!dma->bus_addr) - pci_dma_sync_sg_for_cpu(dev,dma->sglist,dma->nr_pages,dma->direction); + if (!dma->bus_addr && q->ops->vb_dma_sync_sg) + q->ops->vb_dma_sync_sg(dev,dma->sglist,dma->nr_pages, + dma->direction); + return 0; } -int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma) +int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma) { + void *dev=q->dev; + MAGIC_CHECK(dma->magic,MAGIC_DMABUF); if (!dma->sglen) return 0; - if (!dma->bus_addr) - pci_unmap_sg(dev,dma->sglist,dma->nr_pages,dma->direction); + if (!dma->bus_addr && q->ops->vb_unmap_sg) + q->ops->vb_unmap_sg(dev,dma->sglist,dma->nr_pages, + dma->direction); kfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; @@ -318,7 +336,7 @@ int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr) } int -videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb, +videobuf_iolock(struct videobuf_queue* q, struct videobuf_buffer *vb, struct v4l2_framebuffer *fbuf) { int err,pages; @@ -357,7 +375,7 @@ videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb, default: BUG(); } - err = videobuf_dma_pci_map(pci,&vb->dma); + err = videobuf_dma_map(q,&vb->dma); if (0 != err) return err; @@ -366,9 +384,41 @@ videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb, /* --------------------------------------------------------------------- */ +void videobuf_queue_pci(struct videobuf_queue* q) +{ + /* If not specified, defaults to PCI map sg */ + if (!q->ops->vb_map_sg) + q->ops->vb_map_sg=(vb_map_sg_t *)pci_map_sg; + + if (!q->ops->vb_dma_sync_sg) + q->ops->vb_dma_sync_sg=(vb_map_sg_t *)pci_dma_sync_sg_for_cpu; + if (!q->ops->vb_unmap_sg) + q->ops->vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg; +} + +int videobuf_pci_dma_map(struct pci_dev *pci,struct videobuf_dmabuf *dma) +{ + struct videobuf_queue q; + + q.dev=pci; + q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg; + + return (videobuf_dma_unmap(&q,dma)); +} + +int videobuf_pci_dma_unmap(struct pci_dev *pci,struct videobuf_dmabuf *dma) +{ + struct videobuf_queue q; + + q.dev=pci; + q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg; + + return (videobuf_dma_unmap(&q,dma)); +} + void videobuf_queue_init(struct videobuf_queue* q, struct videobuf_queue_ops *ops, - struct pci_dev *pci, + void *dev, spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field, @@ -377,13 +427,15 @@ void videobuf_queue_init(struct videobuf_queue* q, { memset(q,0,sizeof(*q)); q->irqlock = irqlock; - q->pci = pci; + q->dev = dev; q->type = type; q->field = field; q->msize = msize; q->ops = ops; q->priv_data = priv; + videobuf_queue_pci(q); + mutex_init(&q->lock); INIT_LIST_HEAD(&q->stream); } @@ -431,7 +483,8 @@ videobuf_queue_cancel(struct videobuf_queue *q) int i; /* remove queued buffers from list */ - spin_lock_irqsave(q->irqlock,flags); + if (q->irqlock) + spin_lock_irqsave(q->irqlock,flags); for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) continue; @@ -440,7 +493,8 @@ videobuf_queue_cancel(struct videobuf_queue *q) q->bufs[i]->state = STATE_ERROR; } } - spin_unlock_irqrestore(q->irqlock,flags); + if (q->irqlock) + spin_unlock_irqrestore(q->irqlock,flags); /* free all buffers + clear queue */ for (i = 0; i < VIDEO_MAX_FRAME; i++) { @@ -534,19 +588,29 @@ videobuf_reqbufs(struct videobuf_queue *q, unsigned int size,count; int retval; - if (req->type != q->type) + if (req->type != q->type) { + dprintk(1,"reqbufs: queue type invalid\n"); return -EINVAL; - if (req->count < 1) + } + if (req->count < 1) { + dprintk(1,"reqbufs: count invalid (%d)\n",req->count); return -EINVAL; + } if (req->memory != V4L2_MEMORY_MMAP && req->memory != V4L2_MEMORY_USERPTR && - req->memory != V4L2_MEMORY_OVERLAY) + req->memory != V4L2_MEMORY_OVERLAY) { + dprintk(1,"reqbufs: memory type invalid\n"); return -EINVAL; + } - if (q->streaming) + if (q->streaming) { + dprintk(1,"reqbufs: streaming already exists\n"); return -EBUSY; - if (!list_empty(&q->stream)) + } + if (!list_empty(&q->stream)) { + dprintk(1,"reqbufs: stream running\n"); return -EBUSY; + } mutex_lock(&q->lock); count = req->count; @@ -559,8 +623,10 @@ videobuf_reqbufs(struct videobuf_queue *q, count, size, (count*size)>>PAGE_SHIFT); retval = videobuf_mmap_setup(q,count,size,req->memory); - if (retval < 0) + if (retval < 0) { + dprintk(1,"reqbufs: mmap setup returned %d\n",retval); goto done; + } req->count = count; @@ -572,12 +638,18 @@ videobuf_reqbufs(struct videobuf_queue *q, int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) { - if (unlikely(b->type != q->type)) + if (unlikely(b->type != q->type)) { + dprintk(1,"querybuf: Wrong type.\n"); return -EINVAL; - if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) + } + if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) { + dprintk(1,"querybuf: index out of range.\n"); return -EINVAL; - if (unlikely(NULL == q->bufs[b->index])) + } + if (unlikely(NULL == q->bufs[b->index])) { + dprintk(1,"querybuf: buffer is null.\n"); return -EINVAL; + } videobuf_status(b,q->bufs[b->index],q->type); return 0; } @@ -593,26 +665,40 @@ videobuf_qbuf(struct videobuf_queue *q, mutex_lock(&q->lock); retval = -EBUSY; - if (q->reading) + if (q->reading) { + dprintk(1,"qbuf: Reading running...\n"); goto done; + } retval = -EINVAL; - if (b->type != q->type) + if (b->type != q->type) { + dprintk(1,"qbuf: Wrong type.\n"); goto done; - if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) + } + if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) { + dprintk(1,"qbuf: index out of range.\n"); goto done; + } buf = q->bufs[b->index]; - if (NULL == buf) + if (NULL == buf) { + dprintk(1,"qbuf: buffer is null.\n"); goto done; + } MAGIC_CHECK(buf->magic,MAGIC_BUFFER); - if (buf->memory != b->memory) + if (buf->memory != b->memory) { + dprintk(1,"qbuf: memory type is wrong.\n"); goto done; + } if (buf->state == STATE_QUEUED || - buf->state == STATE_ACTIVE) + buf->state == STATE_ACTIVE) { + dprintk(1,"qbuf: buffer is already queued or active.\n"); goto done; + } if (b->flags & V4L2_BUF_FLAG_INPUT) { - if (b->input >= q->inputs) + if (b->input >= q->inputs) { + dprintk(1,"qbuf: wrong input.\n"); goto done; + } buf->input = b->input; } else { buf->input = UNSET; @@ -620,12 +706,16 @@ videobuf_qbuf(struct videobuf_queue *q, switch (b->memory) { case V4L2_MEMORY_MMAP: - if (0 == buf->baddr) + if (0 == buf->baddr) { + dprintk(1,"qbuf: mmap requested but buffer addr is zero!\n"); goto done; + } break; case V4L2_MEMORY_USERPTR: - if (b->length < buf->bsize) + if (b->length < buf->bsize) { + dprintk(1,"qbuf: buffer length is not enough\n"); goto done; + } if (STATE_NEEDS_INIT != buf->state && buf->baddr != b->m.userptr) q->ops->buf_release(q,buf); buf->baddr = b->m.userptr; @@ -634,20 +724,27 @@ videobuf_qbuf(struct videobuf_queue *q, buf->boff = b->m.offset; break; default: + dprintk(1,"qbuf: wrong memory type\n"); goto done; } + dprintk(1,"qbuf: requesting next field\n"); field = videobuf_next_field(q); retval = q->ops->buf_prepare(q,buf,field); - if (0 != retval) + if (0 != retval) { + dprintk(1,"qbuf: buffer_prepare returned %d\n",retval); goto done; + } list_add_tail(&buf->stream,&q->stream); if (q->streaming) { - spin_lock_irqsave(q->irqlock,flags); + if (q->irqlock) + spin_lock_irqsave(q->irqlock,flags); q->ops->buf_queue(q,buf); - spin_unlock_irqrestore(q->irqlock,flags); + if (q->irqlock) + spin_unlock_irqrestore(q->irqlock,flags); } + dprintk(1,"qbuf: succeded\n"); retval = 0; done: @@ -664,26 +761,39 @@ videobuf_dqbuf(struct videobuf_queue *q, mutex_lock(&q->lock); retval = -EBUSY; - if (q->reading) + if (q->reading) { + dprintk(1,"dqbuf: Reading running...\n"); goto done; + } retval = -EINVAL; - if (b->type != q->type) + if (b->type != q->type) { + dprintk(1,"dqbuf: Wrong type.\n"); goto done; - if (list_empty(&q->stream)) + } + if (list_empty(&q->stream)) { + dprintk(1,"dqbuf: stream running\n"); goto done; + } buf = list_entry(q->stream.next, struct videobuf_buffer, stream); retval = videobuf_waiton(buf, nonblocking, 1); - if (retval < 0) + if (retval < 0) { + dprintk(1,"dqbuf: waiton returned %d\n",retval); goto done; + } switch (buf->state) { case STATE_ERROR: + dprintk(1,"dqbuf: state is error\n"); retval = -EIO; - /* fall through */ + videobuf_dma_sync(q,&buf->dma); + buf->state = STATE_IDLE; + break; case STATE_DONE: - videobuf_dma_pci_sync(q->pci,&buf->dma); + dprintk(1,"dqbuf: state is done\n"); + videobuf_dma_sync(q,&buf->dma); buf->state = STATE_IDLE; break; default: + dprintk(1,"dqbuf: state invalid\n"); retval = -EINVAL; goto done; } @@ -711,13 +821,15 @@ int videobuf_streamon(struct videobuf_queue *q) if (q->streaming) goto done; q->streaming = 1; - spin_lock_irqsave(q->irqlock,flags); + if (q->irqlock) + spin_lock_irqsave(q->irqlock,flags); list_for_each(list,&q->stream) { buf = list_entry(list, struct videobuf_buffer, stream); if (buf->state == STATE_PREPARED) q->ops->buf_queue(q,buf); } - spin_unlock_irqrestore(q->irqlock,flags); + if (q->irqlock) + spin_unlock_irqrestore(q->irqlock,flags); done: mutex_unlock(&q->lock); @@ -762,12 +874,14 @@ videobuf_read_zerocopy(struct videobuf_queue *q, char __user *data, goto done; /* start capture & wait */ - spin_lock_irqsave(q->irqlock,flags); + if (q->irqlock) + spin_lock_irqsave(q->irqlock,flags); q->ops->buf_queue(q,q->read_buf); - spin_unlock_irqrestore(q->irqlock,flags); + if (q->irqlock) + spin_unlock_irqrestore(q->irqlock,flags); retval = videobuf_waiton(q->read_buf,0,0); if (0 == retval) { - videobuf_dma_pci_sync(q->pci,&q->read_buf->dma); + videobuf_dma_sync(q,&q->read_buf->dma); if (STATE_ERROR == q->read_buf->state) retval = -EIO; else @@ -809,6 +923,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, /* need to capture a new frame */ retval = -ENOMEM; q->read_buf = videobuf_alloc(q->msize); + dprintk(1,"video alloc=0x%08x\n",(unsigned int) q->read_buf); if (NULL == q->read_buf) goto done; q->read_buf->memory = V4L2_MEMORY_USERPTR; @@ -820,9 +935,11 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, q->read_buf = NULL; goto done; } - spin_lock_irqsave(q->irqlock,flags); + if (q->irqlock) + spin_lock_irqsave(q->irqlock,flags); q->ops->buf_queue(q,q->read_buf); - spin_unlock_irqrestore(q->irqlock,flags); + if (q->irqlock) + spin_unlock_irqrestore(q->irqlock,flags); q->read_off = 0; } @@ -830,7 +947,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, retval = videobuf_waiton(q->read_buf, nonblocking, 1); if (0 != retval) goto done; - videobuf_dma_pci_sync(q->pci,&q->read_buf->dma); + videobuf_dma_sync(q,&q->read_buf->dma); if (STATE_ERROR == q->read_buf->state) { /* catch I/O errors */ @@ -887,10 +1004,12 @@ int videobuf_read_start(struct videobuf_queue *q) return err; list_add_tail(&q->bufs[i]->stream, &q->stream); } - spin_lock_irqsave(q->irqlock,flags); + if (q->irqlock) + spin_lock_irqsave(q->irqlock,flags); for (i = 0; i < count; i++) q->ops->buf_queue(q,q->bufs[i]); - spin_unlock_irqrestore(q->irqlock,flags); + if (q->irqlock) + spin_unlock_irqrestore(q->irqlock,flags); q->reading = 1; return 0; } @@ -985,9 +1104,11 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, if (q->read_off == q->read_buf->size) { list_add_tail(&q->read_buf->stream, &q->stream); - spin_lock_irqsave(q->irqlock,flags); + if (q->irqlock) + spin_lock_irqsave(q->irqlock,flags); q->ops->buf_queue(q,q->read_buf); - spin_unlock_irqrestore(q->irqlock,flags); + if (q->irqlock) + spin_unlock_irqrestore(q->irqlock,flags); q->read_buf = NULL; } if (retval < 0) @@ -1249,11 +1370,14 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init); EXPORT_SYMBOL_GPL(videobuf_dma_init_user); EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); -EXPORT_SYMBOL_GPL(videobuf_dma_pci_map); -EXPORT_SYMBOL_GPL(videobuf_dma_pci_sync); -EXPORT_SYMBOL_GPL(videobuf_dma_pci_unmap); +EXPORT_SYMBOL_GPL(videobuf_dma_map); +EXPORT_SYMBOL_GPL(videobuf_dma_sync); +EXPORT_SYMBOL_GPL(videobuf_dma_unmap); EXPORT_SYMBOL_GPL(videobuf_dma_free); +EXPORT_SYMBOL_GPL(videobuf_pci_dma_map); +EXPORT_SYMBOL_GPL(videobuf_pci_dma_unmap); + EXPORT_SYMBOL_GPL(videobuf_alloc); EXPORT_SYMBOL_GPL(videobuf_waiton); EXPORT_SYMBOL_GPL(videobuf_iolock); diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index e5e749e984ee..4507cb61ae93 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -197,7 +197,8 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, void saa7146_buffer_next(struct saa7146_dev *dev, struct saa7146_dmaqueue *q,int vbi); int saa7146_buffer_queue(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, struct saa7146_buf *buf); void saa7146_buffer_timeout(unsigned long data); -void saa7146_dma_free(struct saa7146_dev *dev,struct saa7146_buf *buf); +void saa7146_dma_free(struct saa7146_dev* dev,struct videobuf_queue *q, + struct saa7146_buf *buf); int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv); int saa7146_vv_release(struct saa7146_dev* dev); diff --git a/include/media/video-buf.h b/include/media/video-buf.h index d90dec5484ee..fff3fd0fbf94 100644 --- a/include/media/video-buf.h +++ b/include/media/video-buf.h @@ -1,15 +1,20 @@ /* * * generic helper functions for video4linux capture buffers, to handle - * memory management and PCI DMA. Right now bttv + saa7134 use it. + * memory management and PCI DMA. + * Right now, bttv, saa7134, saa7146 and cx88 use it. * * The functions expect the hardware being able to scatter gatter * (i.e. the buffers are not linear in physical memory, but fragmented * into PAGE_SIZE chunks). They also assume the driver does not need - * to touch the video data (thus it is probably not useful for USB as - * data often must be uncompressed by the drivers). + * to touch the video data. + * + * device specific map/unmap/sync stuff now are mapped as file operations + * to allow its usage by USB and virtual devices. * * (c) 2001,02 Gerd Knorr + * (c) 2006 Mauro Carvalho Chehab, + * (c) 2006 Ted Walther and John Sokol * * 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 @@ -38,6 +43,9 @@ struct scatterlist* videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages); struct scatterlist* videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset); +struct videobuf_buffer; +struct videobuf_queue; + /* --------------------------------------------------------------------- */ /* @@ -49,7 +57,7 @@ struct scatterlist* videobuf_pages_to_sg(struct page **pages, int nr_pages, * pointer + length. The kernel version just wants the size and * does memory allocation too using vmalloc_32(). * - * videobuf_dma_pci_*() + * videobuf_dma_*() * see Documentation/DMA-mapping.txt, these functions to * basically the same. The map function does also build a * scatterlist for the buffer (and unmap frees it ...) @@ -86,12 +94,18 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, int nr_pages); int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, dma_addr_t addr, int nr_pages); -int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma); -int videobuf_dma_pci_sync(struct pci_dev *dev, - struct videobuf_dmabuf *dma); -int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma); int videobuf_dma_free(struct videobuf_dmabuf *dma); +int videobuf_dma_map(struct videobuf_queue* q,struct videobuf_dmabuf *dma); +int videobuf_dma_sync(struct videobuf_queue* q,struct videobuf_dmabuf *dma); +int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma); + + /*FIXME: these variants are used only on *-alsa code, where videobuf is + * used without queue + */ +int videobuf_pci_dma_map(struct pci_dev *pci,struct videobuf_dmabuf *dma); +int videobuf_pci_dma_unmap(struct pci_dev *pci,struct videobuf_dmabuf *dma); + /* --------------------------------------------------------------------- */ /* @@ -115,9 +129,6 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma); * */ -struct videobuf_buffer; -struct videobuf_queue; - struct videobuf_mapping { unsigned int count; unsigned long start; @@ -164,6 +175,10 @@ struct videobuf_buffer { struct timeval ts; }; +typedef int (vb_map_sg_t)(void *dev,struct scatterlist *sglist,int nr_pages, + int direction); + + struct videobuf_queue_ops { int (*buf_setup)(struct videobuf_queue *q, unsigned int *count, unsigned int *size); @@ -174,12 +189,20 @@ struct videobuf_queue_ops { struct videobuf_buffer *vb); void (*buf_release)(struct videobuf_queue *q, struct videobuf_buffer *vb); + + /* Helper operations - device dependent. + * If null, videobuf_init defaults all to PCI handling + */ + + vb_map_sg_t *vb_map_sg; + vb_map_sg_t *vb_dma_sync_sg; + vb_map_sg_t *vb_unmap_sg; }; struct videobuf_queue { struct mutex lock; spinlock_t *irqlock; - struct pci_dev *pci; + void *dev; /* on pci, points to struct pci_dev */ enum v4l2_buf_type type; unsigned int inputs; /* for V4L2_BUF_FLAG_INPUT */ @@ -204,12 +227,15 @@ struct videobuf_queue { void* videobuf_alloc(unsigned int size); int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr); -int videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb, - struct v4l2_framebuffer *fbuf); +int videobuf_iolock(struct videobuf_queue* q, struct videobuf_buffer *vb, + struct v4l2_framebuffer *fbuf); + +/* Maps fops to PCI stuff */ +void videobuf_queue_pci(struct videobuf_queue* q); void videobuf_queue_init(struct videobuf_queue *q, struct videobuf_queue_ops *ops, - struct pci_dev *pci, + void *dev, spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field, -- cgit v1.2.3 From fa3fcceb30eb8a3cb2ac299c207c6f89b9aed379 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 23 Mar 2006 21:45:24 -0300 Subject: V4L/DVB (3546): Fix Compilation after moving bttv code - Missing a Makefile for bt8xx - rds.h were at wrong directory, since it is a global header for an internal interface - tda7432 and tda9875 were dependent from bttv.h - bttv.h were holding i2c addresses Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/Makefile | 12 ++++++++ drivers/media/video/bt8xx/bttv-driver.c | 2 +- drivers/media/video/bt8xx/bttv.h | 20 +------------ drivers/media/video/rds.h | 48 ------------------------------ drivers/media/video/saa6588.c | 2 +- drivers/media/video/saa7134/saa7134-core.c | 2 +- drivers/media/video/tda7432.c | 2 +- drivers/media/video/tda9875.c | 4 +-- include/media/i2c-addr.h | 30 +++++++++++++++++++ include/media/rds.h | 44 +++++++++++++++++++++++++++ 10 files changed, 93 insertions(+), 73 deletions(-) create mode 100644 drivers/media/video/bt8xx/Makefile delete mode 100644 drivers/media/video/rds.h create mode 100644 include/media/i2c-addr.h create mode 100644 include/media/rds.h (limited to 'include') diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile new file mode 100644 index 000000000000..94350f21cdc0 --- /dev/null +++ b/drivers/media/video/bt8xx/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the video capture/playback device drivers. +# + +bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ + bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \ + bttv-input.o + +obj-$(CONFIG_VIDEO_BT848) += bttv.o + +EXTRA_CFLAGS += -I$(src)/.. +EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 2505ae5a7b97..71535775f2e8 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -42,7 +42,7 @@ #include #include -#include "rds.h" +#include unsigned int bttv_num; /* number of Bt848s in use */ diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 9908c8e0c951..ebde3e8219cf 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -18,6 +18,7 @@ #include #include #include +#include /* ---------------------------------------------------------- */ /* exported by bttv-cards.c */ @@ -168,25 +169,6 @@ #define BTTV_BOARD_HAUPPAUGE_IMPACTVCB 0x8f #define BTTV_BOARD_MACHTV_MAGICTV 0x90 -/* i2c address list */ -#define I2C_TSA5522 0xc2 -#define I2C_TDA7432 0x8a -#define I2C_BT832_ALT1 0x88 -#define I2C_BT832_ALT2 0x8a // alternate setting -#define I2C_TDA8425 0x82 -#define I2C_TDA9840 0x84 -#define I2C_TDA9850 0xb6 /* also used by 9855,9873 */ -#define I2C_TDA9874 0xb0 /* also used by 9875 */ -#define I2C_TDA9875 0xb0 -#define I2C_HAUPEE 0xa0 -#define I2C_STBEE 0xae -#define I2C_VHX 0xc0 -#define I2C_MSP3400 0x80 -#define I2C_MSP3400_ALT 0x88 -#define I2C_TEA6300 0x80 /* also used by 6320 */ -#define I2C_DPL3518 0x84 -#define I2C_TDA9887 0x86 - /* more card-specific defines */ #define PT2254_L_CHANNEL 0x10 #define PT2254_R_CHANNEL 0x08 diff --git a/drivers/media/video/rds.h b/drivers/media/video/rds.h deleted file mode 100644 index 0d30eb744e61..000000000000 --- a/drivers/media/video/rds.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - - Types and defines needed for RDS. This is included by - saa6588.c and every driver (e.g. bttv-driver.c) that wants - to use the saa6588 module. - - Instead of having a seperate rds.h, I'd prefer to include - this stuff in one of the already existing files like tuner.h - - (c) 2005 by Hans J. Koch - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef _RDS_H -#define _RDS_H - -struct rds_command { - unsigned int block_count; - int result; - unsigned char __user *buffer; - struct file *instance; - poll_table *event_list; -}; - -#define RDS_CMD_OPEN _IOW('R',1,int) -#define RDS_CMD_CLOSE _IOW('R',2,int) -#define RDS_CMD_READ _IOR('R',3,int) -#define RDS_CMD_POLL _IOR('R',4,int) - -#endif - - - - diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index d17395c4f55c..a81285ca7d5b 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -32,7 +32,7 @@ #include -#include "rds.h" +#include /* Addresses to scan */ static unsigned short normal_i2c[] = { diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 15405d1e1675..c98571c9d5a6 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -960,7 +960,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, if (saa7134_no_overlay <= 0) { saa7134_video_template.type |= VID_TYPE_OVERLAY; } else { - printk("bttv: Overlay support disabled.\n"); + printk("%s: Overlay support disabled.\n",dev->name); } dev->video_dev = vdev_init(dev,&saa7134_video_template,"video"); err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index fc3d5824efff..600dacb873f4 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -48,9 +48,9 @@ #include #include -#include "bttv.h" #include #include +#include #ifndef VIDEO_AUDIO_BALANCE # define VIDEO_AUDIO_BALANCE 32 diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index ef98c4982250..8a1e58e72c00 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -30,14 +30,14 @@ #include #include -#include "bttv.h" #include +#include + static int debug; /* insmod parameter */ module_param(debug, int, S_IRUGO | S_IWUSR); MODULE_LICENSE("GPL"); - /* Addresses to scan */ static unsigned short normal_i2c[] = { I2C_TDA9875 >> 1, diff --git a/include/media/i2c-addr.h b/include/media/i2c-addr.h new file mode 100644 index 000000000000..4212832d48aa --- /dev/null +++ b/include/media/i2c-addr.h @@ -0,0 +1,30 @@ +/* + * V4L I2C address list + * + * + * Copyright (C) 2006 Mauro Carvalho Chehab + * Based on a previous mapping by + * Ralph Metzler (rjkm@thp.uni-koeln.de) + * Gerd Knorr + * + */ + +/* bttv address list */ +#define I2C_TSA5522 0xc2 +#define I2C_TDA7432 0x8a +#define I2C_BT832_ALT1 0x88 +#define I2C_BT832_ALT2 0x8a // alternate setting +#define I2C_TDA8425 0x82 +#define I2C_TDA9840 0x84 +#define I2C_TDA9850 0xb6 /* also used by 9855,9873 */ +#define I2C_TDA9874 0xb0 /* also used by 9875 */ +#define I2C_TDA9875 0xb0 +#define I2C_HAUPEE 0xa0 +#define I2C_STBEE 0xae +#define I2C_VHX 0xc0 +#define I2C_MSP3400 0x80 +#define I2C_MSP3400_ALT 0x88 +#define I2C_TEA6300 0x80 /* also used by 6320 */ +#define I2C_DPL3518 0x84 +#define I2C_TDA9887 0x86 + diff --git a/include/media/rds.h b/include/media/rds.h new file mode 100644 index 000000000000..951c1ae0be74 --- /dev/null +++ b/include/media/rds.h @@ -0,0 +1,44 @@ +/* + + Types and defines needed for RDS. This is included by + saa6588.c and every driver (e.g. bttv-driver.c) that wants + to use the saa6588 module. + + Instead of having a seperate rds.h, I'd prefer to include + this stuff in one of the already existing files like tuner.h + + (c) 2005 by Hans J. Koch + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef _RDS_H +#define _RDS_H + +struct rds_command { + unsigned int block_count; + int result; + unsigned char __user *buffer; + struct file *instance; + poll_table *event_list; +}; + +#define RDS_CMD_OPEN _IOW('R',1,int) +#define RDS_CMD_CLOSE _IOW('R',2,int) +#define RDS_CMD_READ _IOR('R',3,int) +#define RDS_CMD_POLL _IOR('R',4,int) + +#endif -- cgit v1.2.3 From 7c9b5048305ead226fb16def98d86f3ed67c4807 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 18 Mar 2006 08:08:14 -0300 Subject: V4L/DVB (3547): Tvaudio.h are just i2c addresses. Merged into i2c-addr.h Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tvaudio.c | 2 +- drivers/media/video/tvaudio.h | 14 -------------- include/media/i2c-addr.h | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 15 deletions(-) delete mode 100644 drivers/media/video/tvaudio.h (limited to 'include') diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 4efb01bb44ac..e68d5190cbbb 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -33,7 +33,7 @@ #include #include -#include "tvaudio.h" +#include /* ---------------------------------------------------------------------- */ /* insmod args */ diff --git a/drivers/media/video/tvaudio.h b/drivers/media/video/tvaudio.h deleted file mode 100644 index af7e116af9a9..000000000000 --- a/drivers/media/video/tvaudio.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * i2c bus addresses for the chips supported by tvaudio.c - */ - -#define I2C_TDA8425 0x82 -#define I2C_TDA9840 0x84 /* also used by TA8874Z */ -#define I2C_TDA985x_L 0xb4 /* also used by 9873 */ -#define I2C_TDA985x_H 0xb6 -#define I2C_TDA9874 0xb0 /* also used by 9875 */ - -#define I2C_TEA6300 0x80 /* also used by 6320 */ -#define I2C_TEA6420 0x98 - -#define I2C_PIC16C54 0x96 /* PV951 */ diff --git a/include/media/i2c-addr.h b/include/media/i2c-addr.h index 4212832d48aa..7b0f5fe94e08 100644 --- a/include/media/i2c-addr.h +++ b/include/media/i2c-addr.h @@ -28,3 +28,17 @@ #define I2C_DPL3518 0x84 #define I2C_TDA9887 0x86 +/* + * i2c bus addresses for the chips supported by tvaudio.c + */ + +#define I2C_TDA8425 0x82 +#define I2C_TDA9840 0x84 /* also used by TA8874Z */ +#define I2C_TDA985x_L 0xb4 /* also used by 9873 */ +#define I2C_TDA985x_H 0xb6 +#define I2C_TDA9874 0xb0 /* also used by 9875 */ + +#define I2C_TEA6300 0x80 /* also used by 6320 */ +#define I2C_TEA6420 0x98 + +#define I2C_PIC16C54 0x96 /* PV951 */ -- cgit v1.2.3 From 09df1c163adcf43e2c4234b52985f34b95b7634e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 18 Mar 2006 08:31:34 -0300 Subject: V4L/DVB (3548): Renamed I2C_foo addresses to I2C_ADDR_foo I2C_foo were used for some i2c addresses. Bad, since those constants could mean other i2c chip things. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/bt832.c | 2 +- drivers/media/video/bt8xx/bttv-cards.c | 16 ++++----- drivers/media/video/mxb.c | 6 ++-- drivers/media/video/tda7432.c | 2 +- drivers/media/video/tda9840.c | 2 +- drivers/media/video/tda9840.h | 2 +- drivers/media/video/tda9875.c | 2 +- drivers/media/video/tea6420.c | 2 +- drivers/media/video/tea6420.h | 4 +-- drivers/media/video/tvaudio.c | 60 +++++++++++++++++----------------- include/media/i2c-addr.h | 58 ++++++++++++++++---------------- 11 files changed, 78 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/drivers/media/video/bt8xx/bt832.c b/drivers/media/video/bt8xx/bt832.c index cc54b62f4601..f1309d94e96e 100644 --- a/drivers/media/video/bt8xx/bt832.c +++ b/drivers/media/video/bt8xx/bt832.c @@ -39,7 +39,7 @@ MODULE_LICENSE("GPL"); /* Addresses to scan */ -static unsigned short normal_i2c[] = { I2C_BT832_ALT1>>1, I2C_BT832_ALT2>>1, +static unsigned short normal_i2c[] = { I2C_ADDR_BT832_ALT1>>1, I2C_ADDR_BT832_ALT2>>1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index abfa6ad857a0..e869bfbab378 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -3046,7 +3046,7 @@ static void miro_pinnacle_gpio(struct bttv *btv) gpio_inout(0xffffff, 0); gpio = gpio_read(); id = ((gpio>>10) & 63) -1; - msp = bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx"); + msp = bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx"); if (id < 32) { btv->tuner_type = miro_tunermap[id]; if (0 == (gpio & 0x20)) { @@ -3442,8 +3442,8 @@ void __devinit bttv_init_card2(struct bttv *btv) if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) { /* detect Bt832 chip for quartzsight digital camera */ - if ((bttv_I2CRead(btv, I2C_BT832_ALT1, "Bt832") >=0) || - (bttv_I2CRead(btv, I2C_BT832_ALT2, "Bt832") >=0)) + if ((bttv_I2CRead(btv, I2C_ADDR_BT832_ALT1, "Bt832") >=0) || + (bttv_I2CRead(btv, I2C_ADDR_BT832_ALT2, "Bt832") >=0)) boot_bt832(btv); } @@ -3452,19 +3452,19 @@ void __devinit bttv_init_card2(struct bttv *btv) /* try to detect audio/fader chips */ if (!bttv_tvcards[btv->c.type].no_msp34xx && - bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) + bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx") >=0) request_module("msp3400"); if (bttv_tvcards[btv->c.type].msp34xx_alt && - bttv_I2CRead(btv, I2C_MSP3400_ALT, "MSP34xx (alternate address)") >=0) + bttv_I2CRead(btv, I2C_ADDR_MSP3400_ALT, "MSP34xx (alternate address)") >=0) request_module("msp3400"); if (!bttv_tvcards[btv->c.type].no_tda9875 && - bttv_I2CRead(btv, I2C_TDA9875, "TDA9875") >=0) + bttv_I2CRead(btv, I2C_ADDR_TDA9875, "TDA9875") >=0) request_module("tda9875"); if (!bttv_tvcards[btv->c.type].no_tda7432 && - bttv_I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) + bttv_I2CRead(btv, I2C_ADDR_TDA7432, "TDA7432") >=0) request_module("tda7432"); if (bttv_tvcards[btv->c.type].needs_tvaudio) @@ -3475,7 +3475,7 @@ void __devinit bttv_init_card2(struct bttv *btv) if (btv->tda9887_conf) tda9887 = 1; if (0 == tda9887 && 0 == bttv_tvcards[btv->c.type].has_dvb && - bttv_I2CRead(btv, I2C_TDA9887, "TDA9887") >=0) + bttv_I2CRead(btv, I2C_ADDR_TDA9887, "TDA9887") >=0) tda9887 = 1; /* Hybrid DVB card, DOES have a tda9887 */ if (btv->c.type == BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE) diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index eb3b31867494..14ca251787d5 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -198,13 +198,13 @@ static int mxb_probe(struct saa7146_dev* dev) /* loop through all i2c-devices on the bus and look who is there */ list_for_each(item,&mxb->i2c_adapter.clients) { client = list_entry(item, struct i2c_client, list); - if( I2C_TEA6420_1 == client->addr ) + if( I2C_ADDR_TEA6420_1 == client->addr ) mxb->tea6420_1 = client; - if( I2C_TEA6420_2 == client->addr ) + if( I2C_ADDR_TEA6420_2 == client->addr ) mxb->tea6420_2 = client; if( I2C_TEA6415C_2 == client->addr ) mxb->tea6415c = client; - if( I2C_TDA9840 == client->addr ) + if( I2C_ADDR_TDA9840 == client->addr ) mxb->tda9840 = client; if( I2C_SAA7111 == client->addr ) mxb->saa7111a = client; diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index 600dacb873f4..12b8981efd13 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -71,7 +71,7 @@ module_param(maxvol, int, S_IRUGO | S_IWUSR); /* Address to scan (I2C address of this chip) */ static unsigned short normal_i2c[] = { - I2C_TDA7432 >> 1, + I2C_ADDR_TDA7432 >> 1, I2C_CLIENT_END, }; I2C_CLIENT_INSMOD; diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 0243700f58ae..56e08e156cf0 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -43,7 +43,7 @@ MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); #define TEST 0x04 /* addresses to scan, found only at 0x42 (7-Bit) */ -static unsigned short normal_i2c[] = { I2C_TDA9840, I2C_CLIENT_END }; +static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END }; /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; diff --git a/drivers/media/video/tda9840.h b/drivers/media/video/tda9840.h index 28021053bd09..7da8432cdca7 100644 --- a/drivers/media/video/tda9840.h +++ b/drivers/media/video/tda9840.h @@ -1,7 +1,7 @@ #ifndef __INCLUDED_TDA9840__ #define __INCLUDED_TDA9840__ -#define I2C_TDA9840 0x42 +#define I2C_ADDR_TDA9840 0x42 #define TDA9840_DETECT _IOR('v',1,int) /* return values for TDA9840_DETCT */ diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index 8a1e58e72c00..ccfd3b90ea9f 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -40,7 +40,7 @@ MODULE_LICENSE("GPL"); /* Addresses to scan */ static unsigned short normal_i2c[] = { - I2C_TDA9875 >> 1, + I2C_ADDR_TDA9875 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index ad7d2872cfbf..b4263b630712 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -40,7 +40,7 @@ MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) /* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */ -static unsigned short normal_i2c[] = { I2C_TEA6420_1, I2C_TEA6420_2, I2C_CLIENT_END }; +static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I2C_CLIENT_END }; /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; diff --git a/drivers/media/video/tea6420.h b/drivers/media/video/tea6420.h index ea664df15ad4..5ef7c18e0c54 100644 --- a/drivers/media/video/tea6420.h +++ b/drivers/media/video/tea6420.h @@ -2,8 +2,8 @@ #define __INCLUDED_TEA6420__ /* possible addresses */ -#define I2C_TEA6420_1 0x4c -#define I2C_TEA6420_2 0x4d +#define I2C_ADDR_TEA6420_1 0x4c +#define I2C_ADDR_TEA6420_2 0x4d struct tea6420_multiplex { diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index e68d5190cbbb..4cc0497dcbde 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -137,14 +137,14 @@ struct CHIPSTATE { /* i2c addresses */ static unsigned short normal_i2c[] = { - I2C_TDA8425 >> 1, - I2C_TEA6300 >> 1, - I2C_TEA6420 >> 1, - I2C_TDA9840 >> 1, - I2C_TDA985x_L >> 1, - I2C_TDA985x_H >> 1, - I2C_TDA9874 >> 1, - I2C_PIC16C54 >> 1, + I2C_ADDR_TDA8425 >> 1, + I2C_ADDR_TEA6300 >> 1, + I2C_ADDR_TEA6420 >> 1, + I2C_ADDR_TDA9840 >> 1, + I2C_ADDR_TDA985x_L >> 1, + I2C_ADDR_TDA985x_H >> 1, + I2C_ADDR_TDA9874 >> 1, + I2C_ADDR_PIC16C54 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -1269,8 +1269,8 @@ static struct CHIPDESC chiplist[] = { .name = "tda9840", .id = I2C_DRIVERID_TDA9840, .insmodopt = &tda9840, - .addr_lo = I2C_TDA9840 >> 1, - .addr_hi = I2C_TDA9840 >> 1, + .addr_lo = I2C_ADDR_TDA9840 >> 1, + .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 5, .checkit = tda9840_checkit, @@ -1286,8 +1286,8 @@ static struct CHIPDESC chiplist[] = { .id = I2C_DRIVERID_TDA9873, .checkit = tda9873_checkit, .insmodopt = &tda9873, - .addr_lo = I2C_TDA985x_L >> 1, - .addr_hi = I2C_TDA985x_H >> 1, + .addr_lo = I2C_ADDR_TDA985x_L >> 1, + .addr_hi = I2C_ADDR_TDA985x_H >> 1, .registers = 3, .flags = CHIP_HAS_INPUTSEL, @@ -1308,8 +1308,8 @@ static struct CHIPDESC chiplist[] = { .checkit = tda9874a_checkit, .initialize = tda9874a_initialize, .insmodopt = &tda9874a, - .addr_lo = I2C_TDA9874 >> 1, - .addr_hi = I2C_TDA9874 >> 1, + .addr_lo = I2C_ADDR_TDA9874 >> 1, + .addr_hi = I2C_ADDR_TDA9874 >> 1, .getmode = tda9874a_getmode, .setmode = tda9874a_setmode, @@ -1319,8 +1319,8 @@ static struct CHIPDESC chiplist[] = { .name = "tda9850", .id = I2C_DRIVERID_TDA9850, .insmodopt = &tda9850, - .addr_lo = I2C_TDA985x_L >> 1, - .addr_hi = I2C_TDA985x_H >> 1, + .addr_lo = I2C_ADDR_TDA985x_L >> 1, + .addr_hi = I2C_ADDR_TDA985x_H >> 1, .registers = 11, .getmode = tda985x_getmode, @@ -1332,8 +1332,8 @@ static struct CHIPDESC chiplist[] = { .name = "tda9855", .id = I2C_DRIVERID_TDA9855, .insmodopt = &tda9855, - .addr_lo = I2C_TDA985x_L >> 1, - .addr_hi = I2C_TDA985x_H >> 1, + .addr_lo = I2C_ADDR_TDA985x_L >> 1, + .addr_hi = I2C_ADDR_TDA985x_H >> 1, .registers = 11, .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, @@ -1357,8 +1357,8 @@ static struct CHIPDESC chiplist[] = { .name = "tea6300", .id = I2C_DRIVERID_TEA6300, .insmodopt = &tea6300, - .addr_lo = I2C_TEA6300 >> 1, - .addr_hi = I2C_TEA6300 >> 1, + .addr_lo = I2C_ADDR_TEA6300 >> 1, + .addr_hi = I2C_ADDR_TEA6300 >> 1, .registers = 6, .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, @@ -1379,8 +1379,8 @@ static struct CHIPDESC chiplist[] = { .id = I2C_DRIVERID_TEA6300, .initialize = tea6320_initialize, .insmodopt = &tea6320, - .addr_lo = I2C_TEA6300 >> 1, - .addr_hi = I2C_TEA6300 >> 1, + .addr_lo = I2C_ADDR_TEA6300 >> 1, + .addr_hi = I2C_ADDR_TEA6300 >> 1, .registers = 8, .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, @@ -1400,8 +1400,8 @@ static struct CHIPDESC chiplist[] = { .name = "tea6420", .id = I2C_DRIVERID_TEA6420, .insmodopt = &tea6420, - .addr_lo = I2C_TEA6420 >> 1, - .addr_hi = I2C_TEA6420 >> 1, + .addr_lo = I2C_ADDR_TEA6420 >> 1, + .addr_hi = I2C_ADDR_TEA6420 >> 1, .registers = 1, .flags = CHIP_HAS_INPUTSEL, @@ -1413,8 +1413,8 @@ static struct CHIPDESC chiplist[] = { .name = "tda8425", .id = I2C_DRIVERID_TDA8425, .insmodopt = &tda8425, - .addr_lo = I2C_TDA8425 >> 1, - .addr_hi = I2C_TDA8425 >> 1, + .addr_lo = I2C_ADDR_TDA8425 >> 1, + .addr_hi = I2C_ADDR_TDA8425 >> 1, .registers = 9, .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, @@ -1437,8 +1437,8 @@ static struct CHIPDESC chiplist[] = { .name = "pic16c54 (PV951)", .id = I2C_DRIVERID_PIC16C54_PV9, .insmodopt = &pic16c54, - .addr_lo = I2C_PIC16C54 >> 1, - .addr_hi = I2C_PIC16C54>> 1, + .addr_lo = I2C_ADDR_PIC16C54 >> 1, + .addr_hi = I2C_ADDR_PIC16C54>> 1, .registers = 2, .flags = CHIP_HAS_INPUTSEL, @@ -1456,8 +1456,8 @@ static struct CHIPDESC chiplist[] = { /*.id = I2C_DRIVERID_TA8874Z, */ .checkit = ta8874z_checkit, .insmodopt = &ta8874z, - .addr_lo = I2C_TDA9840 >> 1, - .addr_hi = I2C_TDA9840 >> 1, + .addr_lo = I2C_ADDR_TDA9840 >> 1, + .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 2, .getmode = ta8874z_getmode, diff --git a/include/media/i2c-addr.h b/include/media/i2c-addr.h index 7b0f5fe94e08..e7ff44a35ca0 100644 --- a/include/media/i2c-addr.h +++ b/include/media/i2c-addr.h @@ -1,44 +1,44 @@ /* - * V4L I2C address list + * V4L I2C address list * * - * Copyright (C) 2006 Mauro Carvalho Chehab + * Copyright (C) 2006 Mauro Carvalho Chehab * Based on a previous mapping by - * Ralph Metzler (rjkm@thp.uni-koeln.de) - * Gerd Knorr + * Ralph Metzler (rjkm@thp.uni-koeln.de) + * Gerd Knorr * */ /* bttv address list */ -#define I2C_TSA5522 0xc2 -#define I2C_TDA7432 0x8a -#define I2C_BT832_ALT1 0x88 -#define I2C_BT832_ALT2 0x8a // alternate setting -#define I2C_TDA8425 0x82 -#define I2C_TDA9840 0x84 -#define I2C_TDA9850 0xb6 /* also used by 9855,9873 */ -#define I2C_TDA9874 0xb0 /* also used by 9875 */ -#define I2C_TDA9875 0xb0 -#define I2C_HAUPEE 0xa0 -#define I2C_STBEE 0xae -#define I2C_VHX 0xc0 -#define I2C_MSP3400 0x80 -#define I2C_MSP3400_ALT 0x88 -#define I2C_TEA6300 0x80 /* also used by 6320 */ -#define I2C_DPL3518 0x84 -#define I2C_TDA9887 0x86 +#define I2C_ADDR_TSA5522 0xc2 +#define I2C_ADDR_TDA7432 0x8a +#define I2C_ADDR_BT832_ALT1 0x88 +#define I2C_ADDR_BT832_ALT2 0x8a // alternate setting +#define I2C_ADDR_TDA8425 0x82 +#define I2C_ADDR_TDA9840 0x84 +#define I2C_ADDR_TDA9850 0xb6 /* also used by 9855,9873 */ +#define I2C_ADDR_TDA9874 0xb0 /* also used by 9875 */ +#define I2C_ADDR_TDA9875 0xb0 +#define I2C_ADDR_HAUPEE 0xa0 +#define I2C_ADDR_STBEE 0xae +#define I2C_ADDR_VHX 0xc0 +#define I2C_ADDR_MSP3400 0x80 +#define I2C_ADDR_MSP3400_ALT 0x88 +#define I2C_ADDR_TEA6300 0x80 /* also used by 6320 */ +#define I2C_ADDR_DPL3518 0x84 +#define I2C_ADDR_TDA9887 0x86 /* * i2c bus addresses for the chips supported by tvaudio.c */ -#define I2C_TDA8425 0x82 -#define I2C_TDA9840 0x84 /* also used by TA8874Z */ -#define I2C_TDA985x_L 0xb4 /* also used by 9873 */ -#define I2C_TDA985x_H 0xb6 -#define I2C_TDA9874 0xb0 /* also used by 9875 */ +#define I2C_ADDR_TDA8425 0x82 +#define I2C_ADDR_TDA9840 0x84 /* also used by TA8874Z */ +#define I2C_ADDR_TDA985x_L 0xb4 /* also used by 9873 */ +#define I2C_ADDR_TDA985x_H 0xb6 +#define I2C_ADDR_TDA9874 0xb0 /* also used by 9875 */ -#define I2C_TEA6300 0x80 /* also used by 6320 */ -#define I2C_TEA6420 0x98 +#define I2C_ADDR_TEA6300 0x80 /* also used by 6320 */ +#define I2C_ADDR_TEA6420 0x98 -#define I2C_PIC16C54 0x96 /* PV951 */ +#define I2C_ADDR_PIC16C54 0x96 /* PV951 */ -- cgit v1.2.3 From 8bf2f8e747700419cc5bbc56c4496774eb8f2f1f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 18 Mar 2006 21:31:00 -0300 Subject: V4L/DVB (3577): Cleanup audio input handling Cleanup audio input handling in bttv and tvaudio: - inputs were specified that were never used - mute was handled as a special input which led to confusing code - confusing naming made it difficult to see if the setting was for i2c or gpio. The old audiochip.h input names moved to tvaudio.h. Currently this is used both by tvaudio and msp3400 until the msp3400 implements the new msp3400-specific inputs. Detect in bttv the tvaudio and msp3400 i2c clients and use these client pointers to set the inputs directly instead of broadcasting the command. Removed AUDC_SET_INPUT. Now replaced by VIDIOC_S_AUDIO. This will be replaced again later by the new ROUTING commands. Removed VIDIOC_G_AUDIO implementations in i2c drivers: this command is a user level command and not to be used internally. It wasn't called at all anyway. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/bt832.c | 1 - drivers/media/video/bt8xx/bttv-cards.c | 299 +++++++++++++++++----------- drivers/media/video/bt8xx/bttv-driver.c | 91 +++++---- drivers/media/video/bt8xx/bttv-i2c.c | 4 + drivers/media/video/bt8xx/bttv.h | 3 +- drivers/media/video/bt8xx/bttvp.h | 4 +- drivers/media/video/cs53l32a.c | 5 - drivers/media/video/cx25840/cx25840-audio.c | 1 - drivers/media/video/cx25840/cx25840-core.c | 11 - drivers/media/video/cx88/cx88.h | 1 - drivers/media/video/msp3400-driver.c | 104 +--------- drivers/media/video/msp3400-kthreads.c | 1 - drivers/media/video/saa7115.c | 1 - drivers/media/video/saa7134/saa7134.h | 1 - drivers/media/video/tda7432.c | 1 - drivers/media/video/tda9875.c | 3 - drivers/media/video/tuner-core.c | 1 - drivers/media/video/tvaudio.c | 67 +++++-- drivers/media/video/tveeprom.c | 30 +-- drivers/media/video/v4l2-common.c | 2 - drivers/media/video/wm8775.c | 5 - include/media/audiochip.h | 14 -- include/media/tvaudio.h | 30 +++ include/media/v4l2-common.h | 11 +- 24 files changed, 350 insertions(+), 341 deletions(-) create mode 100644 include/media/tvaudio.h (limited to 'include') diff --git a/drivers/media/video/bt8xx/bt832.c b/drivers/media/video/bt8xx/bt832.c index f1309d94e96e..a51876137880 100644 --- a/drivers/media/video/bt8xx/bt832.c +++ b/drivers/media/video/bt8xx/bt832.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include "bttv.h" diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index e869bfbab378..f209a7492051 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -39,6 +39,7 @@ #include "bttvp.h" #include +#include /* fwd decl */ static void boot_msp34xx(struct bttv *btv, int pin); @@ -336,7 +337,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 15, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 2, 0, 0, 0, 10 }, + .gpiomux = { 2, 0, 0, 0 }, + .gpiomute = 10, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -350,7 +352,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 7, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 1, 2, 3, 4 }, + .gpiomux = { 0, 1, 2, 3 }, + .gpiomute = 4, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -364,7 +367,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 7, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 4, 0, 2, 3, 1 }, + .gpiomux = { 4, 0, 2, 3 }, + .gpiomute = 1, .no_msp34xx = 1, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_NTSC, @@ -383,7 +387,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .needs_tvaudio = 0, .tuner_type = 4, .tuner_addr = ADDR_UNSET, @@ -397,7 +401,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 3, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 0, 1, 0, 1, 3 }, + .gpiomux = { 0, 1, 0, 1 }, + .gpiomute = 3, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -411,7 +416,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 3, .muxsel = { 2, 3, 1, 1 }, .gpiomask = 0x0f, - .audiomux = { 0x0c, 0x04, 0x08, 0x04, 0 }, + .gpiomux = { 0x0c, 0x04, 0x08, 0x04 }, /* 0x04 for some cards ?? */ .needs_tvaudio = 1, .tuner_type = -1, @@ -428,7 +433,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 3, .gpiomask = 0, .muxsel = { 2, 3, 1, 0, 0 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -444,7 +449,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xc00, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0xc00, 0x800, 0x400, 0xc00, 0 }, + .gpiomux = { 0, 0xc00, 0x800, 0x400 }, + .gpiomute = 0xc00, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -459,7 +465,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 3, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 1, 1, 2, 3, 0 }, + .gpiomux = { 1, 1, 2, 3 }, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_TEMIC_PAL, @@ -474,7 +480,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x0f, /* old: 7 */ .muxsel = { 2, 0, 1, 1 }, - .audiomux = { 0, 1, 2, 3, 4 }, + .gpiomux = { 0, 1, 2, 3 }, + .gpiomute = 4, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -489,7 +496,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x3014f, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x20001,0x10001, 0, 0,10 }, + .gpiomux = { 0x20001,0x10001, 0, 0 }, + .gpiomute = 10, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -505,7 +513,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 15, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 13, 14, 11, 7, 0, 0 }, + .gpiomux = { 13, 14, 11, 7 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -519,7 +527,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 15, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 13, 14, 11, 7, 0, 0 }, + .gpiomux = { 13, 14, 11, 7 }, .needs_tvaudio = 1, .msp34xx_alt = 1, .pll = PLL_28, @@ -537,7 +545,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 7, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 2, 1, 3, 4 }, /* old: {0, 1, 2, 3, 4} */ + .gpiomux = { 0, 2, 1, 3 }, /* old: {0, 1, 2, 3, 4} */ + .gpiomute = 4, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -552,7 +561,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 15, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0 , 0, 1 , 0, 10 }, + .gpiomux = { 0, 0, 1, 0 }, + .gpiomute = 10, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -570,10 +580,11 @@ struct tvcard bttv_tvcards[] = { .muxsel = { 2, 3, 1, 1 }, #if 0 /* old */ - .audiomux = { 0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 }, + .gpiomux = { 0x01c000, 0, 0x018000, 0x014000, 0x002000 }, #else /* 2003-10-20 by "Anton A. Arapov" */ - .audiomux = { 0x001e00, 0, 0x018000, 0x014000, 0x002000, 0 }, + .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 }, + .gpiomute = 0x002000, #endif .needs_tvaudio = 1, .pll = PLL_28, @@ -587,7 +598,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x8300f8, .muxsel = { 2, 3, 1, 1,0 }, - .audiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007 }, + .gpiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007 }, + .gpiomute = 0xcfa007, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -603,7 +615,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 1, 0, 0, 0, 0 }, + .gpiomux = { 1, 0, 0, 0 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -617,7 +629,7 @@ struct tvcard bttv_tvcards[] = { .svhs = -1, .gpiomask = 0x8dff00, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .no_msp34xx = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -644,7 +656,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1800, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800 }, + .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, + .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, @@ -658,7 +671,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xc00, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 1, 0x800, 0x400, 0xc00, 0 }, + .gpiomux = { 0, 1, 0x800, 0x400 }, + .gpiomute = 0xc00, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -674,7 +688,7 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 7, .muxsel = { 2, 3, -1 }, .digital_mode = DIGITAL_MODE_CAMERA, - .audiomux = { 0, 0, 0, 0, 0 }, + .gpiomux = { 0, 0, 0, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_ALPS_TSBB5_PAL_I, @@ -691,7 +705,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xe00, .muxsel = { 2, 3, 1, 1 }, - .audiomux = {0x400, 0x400, 0x400, 0x400, 0xc00 }, + .gpiomux = {0x400, 0x400, 0x400, 0x400 }, + .gpiomute = 0xc00, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -707,7 +722,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1f0fff, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x20000, 0x30000, 0x10000, 0, 0x40000 }, + .gpiomux = { 0x20000, 0x30000, 0x10000, 0 }, + .gpiomute = 0x40000, .needs_tvaudio = 0, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -722,7 +738,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 3, .gpiomask = 7, .muxsel = { 2, 0, 1, 1 }, - .audiomux = { 0, 1, 2, 3, 4 }, + .gpiomux = { 0, 1, 2, 3 }, + .gpiomute = 4, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -736,7 +753,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1800, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800 }, + .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, + .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_SECAM, .tuner_addr = ADDR_UNSET, @@ -752,7 +770,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1f0fff, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x20000, 0x30000, 0x10000, 0x00000, 0x40000 }, + .gpiomux = { 0x20000, 0x30000, 0x10000, 0x00000 }, + .gpiomute = 0x40000, .needs_tvaudio = 0, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -799,7 +818,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 1, /* was: 4 */ .gpiomask = 0, .muxsel = { 2, 3, 1, 0, 0}, - .audiomux = { 0 }, + .gpiomux = { 0 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -815,7 +834,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1800, /* 0x8dfe00 */ .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0x0800, 0x1000, 0x1000, 0x1800, 0 }, + .gpiomux = { 0, 0x0800, 0x1000, 0x1000 }, + .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -829,7 +849,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 3, .gpiomask = 1, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 1, 0, 0, 0, 0 }, + .gpiomux = { 1, 0, 0, 0 }, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -845,7 +865,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .needs_tvaudio = 0, .tuner_type = 4, .tuner_addr = ADDR_UNSET, @@ -859,7 +879,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xffff00, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x500, 0, 0x300, 0x900, 0x900 }, + .gpiomux = { 0x500, 0, 0x300, 0x900 }, + .gpiomute = 0x900, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -875,11 +896,12 @@ struct tvcard bttv_tvcards[] = { .muxsel = { 2, 3, 1, 1, 0 }, /* TV, CVid, SVid, CVid over SVid connector */ #if 0 .gpiomask = 0xc33000, - .audiomux = { 0x422000,0x1000,0x0000,0x620000,0x800000 }, + .gpiomux = { 0x422000,0x1000,0x0000,0x620000,0x800000 }, #else /* Alexander Varakin [stereo version] */ .gpiomask = 0xb33000, - .audiomux = { 0x122000,0x1000,0x0000,0x620000,0x800000 }, + .gpiomux = { 0x122000,0x1000,0x0000,0x620000 }, + .gpiomute = 0x800000, #endif /* Audio Routing for "WinFast 2000 XP" (no tv stereo !) gpio23 -- hef4052:nEnable (0x800000) @@ -909,7 +931,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1800, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800 }, + .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, + .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -925,7 +948,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1800, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 }, + .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, + .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -940,7 +964,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xff, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x21, 0x20, 0x24, 0x2c, 0x29, 0x29 }, + .gpiomux = { 0x21, 0x20, 0x24, 0x2c }, + .gpiomute = 0x29, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = -1, @@ -955,7 +980,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x551e00, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 0x551400, 0x551200, 0, 0, 0x551c00, 0x551200 }, + .gpiomux = { 0x551400, 0x551200, 0, 0 }, + .gpiomute = 0x551c00, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = 1, @@ -971,7 +997,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x03000F, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 2, 0xd0001, 0, 0, 1 }, + .gpiomux = { 2, 0xd0001, 0, 0 }, + .gpiomute = 1, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = -1, @@ -988,7 +1015,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 7, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 4, 0, 2, 3, 1 }, + .gpiomux = { 4, 0, 2, 3 }, + .gpiomute = 1, .no_msp34xx = 1, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_NTSC, @@ -1005,7 +1033,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 15, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 13, 4, 11, 7, 0, 0 }, + .gpiomux = { 13, 4, 11, 7 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -1022,7 +1050,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0, .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0, 0, 0, 0, 0}, + .gpiomux = { 0, 0, 0, 0}, .needs_tvaudio = 1, .no_msp34xx = 1, .pll = PLL_28, @@ -1038,7 +1066,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xe00b, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0, 0xff3ffc }, + .gpiomux = { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0 }, + .gpiomute = 0xff3ffc, .no_msp34xx = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -1054,7 +1083,8 @@ struct tvcard bttv_tvcards[] = { .svhs = -1, .gpiomask = 3, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 1, 1, 0, 2, 3 }, + .gpiomux = { 1, 1, 0, 2 }, + .gpiomute = 3, .no_msp34xx = 1, .pll = PLL_NONE, .tuner_type = -1, @@ -1069,7 +1099,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 3, .gpiomask = 0, .muxsel = { 2, 3, 1, 0, 0 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = -1, @@ -1084,7 +1114,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xbcf03f, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0xbc803f, 0xbc903f, 0xbcb03f, 0, 0xbcb03f }, + .gpiomux = { 0xbc803f, 0xbc903f, 0xbcb03f, 0 }, + .gpiomute = 0xbcb03f, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = 21, @@ -1099,7 +1130,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x70000, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x20000, 0x30000, 0x10000, 0, 0x40000, 0x20000 }, + .gpiomux = { 0x20000, 0x30000, 0x10000, 0 }, + .gpiomute = 0x40000, .needs_tvaudio = 1, .no_msp34xx = 1, .pll = PLL_35, @@ -1118,7 +1150,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 15, .muxsel = { 2, 3, 1, 1 }, - .audiomux = {2,0,0,0,1 }, + .gpiomux = {2,0,0,0 }, + .gpiomute = 1, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -1133,7 +1166,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x010f00, .muxsel = {2, 3, 0, 0 }, - .audiomux = {0x10000, 0, 0x10000, 0, 0, 0 }, + .gpiomux = {0x10000, 0, 0x10000, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_ALPS_TSHC6_NTSC, @@ -1150,7 +1183,8 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0xAA0000, .muxsel = { 2,3,1,1,-1 }, .digital_mode = DIGITAL_MODE_CAMERA, - .audiomux = { 0x20000, 0, 0x80000, 0x80000, 0xa8000, 0x46000 }, + .gpiomux = { 0x20000, 0, 0x80000, 0x80000 }, + .gpiomute = 0xa8000, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL_I, @@ -1175,7 +1209,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 7, .muxsel = { 2, 0, 1, 1 }, - .audiomux = { 0, 1, 2, 3, 4 }, + .gpiomux = { 0, 1, 2, 3 }, + .gpiomute = 4, .pll = PLL_28, .tuner_type = -1 /* TUNER_ALPS_TMDH2_NTSC */, .tuner_addr = ADDR_UNSET, @@ -1192,7 +1227,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 3, .gpiomask = 0x03000F, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 1, 0xd0001, 0, 0, 10 }, + .gpiomux = { 1, 0xd0001, 0, 0 }, + .gpiomute = 10, /* sound path (5 sources): MUX1 (mask 0x03), Enable Pin 0x08 (0=enable, 1=disable) 0= ext. Audio IN @@ -1218,7 +1254,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1c, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0, 0x10, 8, 4 }, + .gpiomux = { 0, 0, 0x10, 8 }, + .gpiomute = 4, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -1230,7 +1267,7 @@ struct tvcard bttv_tvcards[] = { /* Tim Röstermundt in de.comp.os.unix.linux.hardware: options bttv card=0 pll=1 radio=1 gpiomask=0x18e0 - audiomux=0x44c71f,0x44d71f,0,0x44d71f,0x44dfff + gpiomux =0x44c71f,0x44d71f,0,0x44d71f,0x44dfff options tuner type=5 */ .name = "Lifeview FlyVideo 2000 /FlyVideo A2/ Lifetec LT 9415 TV [LR90]", .video_inputs = 4, @@ -1239,7 +1276,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x18e0, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x0000,0x0800,0x1000,0x1000,0x18e0 }, + .gpiomux = { 0x0000,0x0800,0x1000,0x1000 }, + .gpiomute = 0x18e0, /* For cards with tda9820/tda9821: 0x0000: Tuner normal stereo 0x0080: Tuner A2 SAP (second audio program = Zweikanalton) @@ -1259,7 +1297,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xF, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 2, 0, 0, 0, 10 }, + .gpiomux = { 2, 0, 0, 0 }, + .gpiomute = 10, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_TEMIC_PAL, @@ -1277,7 +1316,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1800, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 }, + .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, + .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = 5, .tuner_addr = ADDR_UNSET, @@ -1294,7 +1334,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 1, .gpiomask = 0, .muxsel = { 3, 1 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_35, @@ -1311,7 +1351,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xe00, .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0x400, 0x400, 0x400, 0x400, 0x800, 0x400 }, + .gpiomux = { 0x400, 0x400, 0x400, 0x400 }, + .gpiomute = 0x800, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_TEMIC_4036FY5_NTSC, @@ -1327,7 +1368,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x03000F, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 2, 0, 0, 0, 1 }, + .gpiomux = { 2, 0, 0, 0 }, + .gpiomute = 1, .pll = PLL_28, .tuner_type = 0, .tuner_addr = ADDR_UNSET, @@ -1344,7 +1386,8 @@ struct tvcard bttv_tvcards[] = { .svhs = -1, .gpiomask = 11, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 2, 0, 0, 1, 8 }, + .gpiomux = { 2, 0, 0, 1 }, + .gpiomute = 8, .pll = PLL_35, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, @@ -1359,7 +1402,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 1, .gpiomask = 0xF, .muxsel = { 2, 2 }, - .audiomux = { }, + .gpiomux = { }, .no_msp34xx = 1, .needs_tvaudio = 0, .pll = PLL_28, @@ -1378,7 +1421,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xFF, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 1, 0, 4, 4, 9 }, + .gpiomux = { 1, 0, 4, 4 }, + .gpiomute = 9, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -1394,7 +1438,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xf03f, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 0xbffe, 0, 0xbfff, 0, 0xbffe }, + .gpiomux = { 0xbffe, 0, 0xbfff, 0 }, + .gpiomute = 0xbffe, .pll = PLL_28, .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL, .tuner_addr = ADDR_UNSET, @@ -1411,7 +1456,7 @@ struct tvcard bttv_tvcards[] = { .svhs = -1, .gpiomask = 1, .muxsel = { 2, 3, 0, 1 }, - .audiomux = { 0, 0, 1, 0, 0 }, + .gpiomux = { 0, 0, 1, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL, @@ -1430,7 +1475,8 @@ struct tvcard bttv_tvcards[] = { /* Radio changed from 1e80 to 0x800 to make FlyVideo2000S in .hu happy (gm)*/ /* -dk-???: set mute=0x1800 for tda9874h daughterboard */ - .audiomux = { 0x0000,0x0800,0x1000,0x1000,0x1800, 0x1080 }, + .gpiomux = { 0x0000,0x0800,0x1000,0x1000 }, + .gpiomute = 0x1800, .audio_hook = fv2000s_audio, .no_msp34xx = 1, .no_tda9875 = 1, @@ -1448,7 +1494,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xffff00, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x500, 0x500, 0x300, 0x900, 0x900 }, + .gpiomux = { 0x500, 0x500, 0x300, 0x900 }, + .gpiomute = 0x900, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -1465,7 +1512,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x010f00, .muxsel = {2, 3, 0, 0 }, - .audiomux = {0x10000, 0, 0x10000, 0, 0, 0 }, + .gpiomux = {0x10000, 0, 0x10000, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_SHARP_2U5JF5540_NTSC, @@ -1486,7 +1533,8 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0x4f8a00, /* 0x100000: 1=MSP enabled (0=disable again) * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */ - .audiomux = {0x947fff, 0x987fff,0x947fff,0x947fff, 0x947fff}, + .gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff }, + .gpiomute = 0x947fff, /* tvtuner, radio, external,internal, mute, stereo * tuner, Composit, SVid, Composit-on-Svid-adapter */ .muxsel = { 2, 3 ,0 ,1 }, @@ -1518,7 +1566,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 15, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0, 11, 7, 13, 0 }, /* TV and Radio with same GPIO ! */ + .gpiomux = { 0, 0, 11, 7 }, /* TV and Radio with same GPIO ! */ + .gpiomute = 13, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = 25, @@ -1557,7 +1606,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x3f, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x01, 0x00, 0x03, 0x03, 0x09, 0x02 }, + .gpiomux = { 0x01, 0x00, 0x03, 0x03 }, + .gpiomute = 0x09, .needs_tvaudio = 1, .no_msp34xx = 1, .no_tda9875 = 1, @@ -1586,7 +1636,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 4, .gpiomask = 0, .muxsel = { 2, 3, 1, 0, 0 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .needs_tvaudio = 0, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -1618,7 +1668,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x1C800F, /* Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset */ .muxsel = { 2, 1, 1, }, - .audiomux = { 0, 1, 2, 2, 4 }, + .gpiomux = { 0, 1, 2, 2 }, + .gpiomute = 4, .needs_tvaudio = 0, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -1637,7 +1688,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x140007, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 1, 2, 3, 4, 0 }, + .gpiomux = { 0, 1, 2, 3 }, + .gpiomute = 4, .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -1651,7 +1703,7 @@ struct tvcard bttv_tvcards[] = { .svhs = -1, .gpiomask = 0, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, @@ -1667,13 +1719,14 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 7, .muxsel = { 2, 3, 1, 1 }, /* Tuner, SVid, SVHS, SVid to SVHS connector */ - .audiomux = { 0 ,0 ,4, 4,4,4},/* Yes, this tuner uses the same audio output for TV and FM radio! + .gpiomux = { 0, 0, 4, 4 },/* Yes, this tuner uses the same audio output for TV and FM radio! * This card lacks external Audio In, so we mute it on Ext. & Int. * The PCB can take a sbx1637/sbx1673, wiring unknown. * This card lacks PCI subsystem ID, sigh. - * audiomux=1: lower volume, 2+3: mute + * gpiomux =1: lower volume, 2+3: mute * btwincap uses 0x80000/0x80003 */ + .gpiomute = 4, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, @@ -1720,7 +1773,7 @@ struct tvcard bttv_tvcards[] = { .radio_addr = ADDR_UNSET, .gpiomask = 7, - .audiomux = {7}, + .gpiomux = {7}, }, [BTTV_BOARD_GVBCTV5PCI] = { .name = "IODATA GV-BCTV5/PCI", @@ -1730,7 +1783,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x0f0f80, .muxsel = {2, 3, 1, 0 }, - .audiomux = {0x030000, 0x010000, 0, 0, 0x020000, 0}, + .gpiomux = {0x030000, 0x010000, 0, 0 }, + .gpiomute = 0x020000, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_NTSC_M, @@ -1960,7 +2014,7 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 2, /* TV, Comp1, Composite over SVID con, SVID */ .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 2, 2, 0, 0, 0 }, + .gpiomux = { 2, 2, 0, 0 }, .pll = PLL_28, .has_radio = 1, .tuner_type = TUNER_PHILIPS_PAL, @@ -1984,7 +2038,8 @@ struct tvcard bttv_tvcards[] = { .svhs = -1, .gpiomask = 7, .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0, 1, 2, 3, 4}, + .gpiomux = { 0, 1, 2, 3}, + .gpiomute = 4, .needs_tvaudio = 1, .tuner_type = 5, .tuner_addr = ADDR_UNSET, @@ -2016,7 +2071,7 @@ struct tvcard bttv_tvcards[] = { .svhs = -1, .gpiomask = 0, .muxsel = { 2, 3 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, @@ -2035,7 +2090,8 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0x001e8007, .muxsel = { 2, 3, 1, 0 }, /* Tuner, Radio, external, internal, off, on */ - .audiomux = { 0x08, 0x0f, 0x0a, 0x08, 0x0f, 0x08 }, + .gpiomux = { 0x08, 0x0f, 0x0a, 0x08 }, + .gpiomute = 0x0f, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, @@ -2152,7 +2208,7 @@ struct tvcard bttv_tvcards[] = { .svhs = -1, .gpiomask = 0, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 0 }, + .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, @@ -2169,7 +2225,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 3, .gpiomask = 0x00, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 0, 0, 0, 0, 0, 0 }, /* card has no audio */ + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -2184,7 +2240,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 3, .gpiomask = 0x00, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 0, 0, 0, 0, 0 }, /* card has no audio */ + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -2204,7 +2260,7 @@ struct tvcard bttv_tvcards[] = { via the upper nibble of muxsel. here: used for xternal video-mux */ .muxsel = { 0x02, 0x12, 0x22, 0x32, 0x03, 0x13, 0x23, 0x33, 0x01, 0x00 }, - .audiomux = { 0, 0, 0, 0, 0, 0 }, /* card has no audio */ + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -2222,7 +2278,7 @@ struct tvcard bttv_tvcards[] = { via the upper nibble of muxsel. here: used for xternal video-mux */ .muxsel = { 0x02, 0x12, 0x22, 0x32, 0x03, 0x13, 0x23, 0x33, 0x01, 0x01 }, - .audiomux = { 0, 0, 0, 0, 0, 0 }, /* card has no audio */ + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -2310,7 +2366,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 3, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 1, 1, 1, 1, 0 }, + .gpiomux = { 1, 1, 1, 1 }, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -2341,7 +2397,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x008007, .muxsel = { 2, 3, 0, 0 }, - .audiomux = { 0, 0, 0, 0, 0x000003, 0 }, + .gpiomux = { 0, 0, 0, 0 }, + .gpiomute = 0x000003, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -2377,7 +2434,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 0, .gpiomask = 0x68, .muxsel = { 2, 3, 1 }, - .audiomux = { 0x68, 0x68, 0x61, 0x61, 0x00 }, + .gpiomux = { 0x68, 0x68, 0x61, 0x61 }, .pll = PLL_28, }, @@ -2392,7 +2449,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x008007, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 1, 2, 2, 3 }, + .gpiomux = { 0, 1, 2, 2 }, + .gpiomute = 3, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -2417,7 +2475,7 @@ struct tvcard bttv_tvcards[] = { .no_tda9875 = 1, .no_tda7432 = 1, .muxsel = {2,2,2,2},/*878A input is always MUX0, see above.*/ - .audiomux = { 0, 0, 0, 0, 0, 0 }, /* card has no audio */ + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .pll = PLL_28, .needs_tvaudio = 0, .muxsel_hook = picolo_tetra_muxsel,/*Required as it doesn't follow the classic input selection policy*/ @@ -2435,7 +2493,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x0000000f, .muxsel = { 2, 1, 1 }, - .audiomux = { 0x02, 0x00, 0x00, 0x00, 0x00 }, + .gpiomux = { 0x02, 0x00, 0x00, 0x00 }, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -2491,7 +2549,7 @@ struct tvcard bttv_tvcards[] = { .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }, .muxsel_hook = sigmaSQ_muxsel, - .audiomux = { 0 }, + .gpiomux = { 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = -1, @@ -2508,7 +2566,7 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0x0, .muxsel = { 2, 2, 2, 2 }, .muxsel_hook = sigmaSLC_muxsel, - .audiomux = { 0 }, + .gpiomux = { 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = -1, @@ -2526,7 +2584,8 @@ struct tvcard bttv_tvcards[] = { .svhs = -1, .gpiomask = 0xFF, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 2, 0, 0, 0, 10 }, + .gpiomux = { 2, 0, 0, 0 }, + .gpiomute = 10, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -2560,7 +2619,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x3f, .muxsel = {2, 3, 1, 0 }, - .audiomux = {0x31, 0x31, 0x31, 0x31, 0x31, 0x31 }, + .gpiomux = {0x31, 0x31, 0x31, 0x31 }, + .gpiomute = 0x31, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_NTSC_M, @@ -2583,7 +2643,7 @@ struct tvcard bttv_tvcards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .gpiomask = 0x008007, - .audiomux = { 0, 0x000001,0,0, 0 }, + .gpiomux = { 0, 0x000001,0,0 }, .needs_tvaudio = 1, .has_radio = 1, }, @@ -2693,7 +2753,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .muxsel = { 2, 3, 1 }, .gpiomask = 0x00e00007, - .audiomux = { 0x00400005, 0, 0x00000001, 0, 0x00c00007, 0 }, + .gpiomux = { 0x00400005, 0, 0x00000001, 0 }, + .gpiomute = 0x00c00007, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -2709,7 +2770,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x01fe00, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x001e00, 0, 0x018000, 0x014000, 0x002000, 0 }, + .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 }, + .gpiomute = 0x002000, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_YMEC_TVF66T5_B_DFF, @@ -2726,7 +2788,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x001c0007, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 1, 2, 2, 3 }, + .gpiomux = { 0, 1, 2, 2 }, + .gpiomute = 3, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_TENA_9533_DI, @@ -2745,7 +2808,8 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0x01fe00, .muxsel = { 2,3,1,1,-1 }, .digital_mode = DIGITAL_MODE_CAMERA, - .audiomux = { 0x00400, 0x10400, 0x04400, 0x80000, 0x12400, 0x46000 }, + .gpiomux = { 0x00400, 0x10400, 0x04400, 0x80000 }, + .gpiomute = 0x12400, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_LG_PAL_FM, @@ -2763,7 +2827,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x3f, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0x21, 0x20, 0x24, 0x2c, 0x29, 0x29 }, + .gpiomux = { 0x21, 0x20, 0x24, 0x2c }, + .gpiomute = 0x29, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_YMEC_TVF_5533MF, @@ -2797,7 +2862,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 15, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 2, 0, 0, 0, 1 }, + .gpiomux = { 2, 0, 0, 0 }, + .gpiomute = 1, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = 2, @@ -2813,7 +2879,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x108007, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 100000, 100002, 100002, 100000 }, + .gpiomux = { 100000, 100002, 100002, 100000 }, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -2853,7 +2919,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 7, .muxsel = { 2, 3, 1, 1 }, - .audiomux = { 0, 1, 2, 3, 4 }, + .gpiomux = { 0, 1, 2, 3 }, + .gpiomute = 4, .tuner_type = TUNER_TEMIC_4009FR5_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -2925,20 +2992,20 @@ void __devinit bttv_idcard(struct bttv *btv) if (UNSET != audiomux[0]) { gpiobits = 0; for (i = 0; i < 5; i++) { - bttv_tvcards[btv->c.type].audiomux[i] = audiomux[i]; + bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i]; gpiobits |= audiomux[i]; } } else { gpiobits = audioall; for (i = 0; i < 5; i++) { - bttv_tvcards[btv->c.type].audiomux[i] = audioall; + bttv_tvcards[btv->c.type].gpiomux[i] = audioall; } } bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits; printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=", btv->c.nr,bttv_tvcards[btv->c.type].gpiomask); for (i = 0; i < 5; i++) { - printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].audiomux[i]); + printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]); } printk("\n"); } @@ -3796,18 +3863,18 @@ void bttv_tda9880_setnorm(struct bttv *btv, int norm) { /* fix up our card entry */ if(norm==VIDEO_MODE_NTSC) { - bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].audiomux[0]=0x957fff; - bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].audiomux[4]=0x957fff; + bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomux[TVAUDIO_INPUT_TUNER]=0x957fff; + bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomute=0x957fff; dprintk("bttv_tda9880_setnorm to NTSC\n"); } else { - bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].audiomux[0]=0x947fff; - bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].audiomux[4]=0x947fff; + bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomux[TVAUDIO_INPUT_TUNER]=0x947fff; + bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomute=0x947fff; dprintk("bttv_tda9880_setnorm to PAL\n"); } /* set GPIO according */ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, - bttv_tvcards[btv->c.type].audiomux[btv->audio]); + bttv_tvcards[btv->c.type].gpiomux[btv->audio]); } diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 71535775f2e8..be567ec9e145 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -36,6 +36,7 @@ #include #include "bttvp.h" #include +#include #include @@ -926,45 +927,65 @@ video_mux(struct bttv *btv, unsigned int input) static char *audio_modes[] = { "audio: tuner", "audio: radio", "audio: extern", - "audio: intern", "audio: off" + "audio: intern", "audio: mute" }; static int -audio_mux(struct bttv *btv, int mode) +audio_mux(struct bttv *btv, int input, int mute) { - int val,mux,i2c_mux,signal; + int gpio_val, signal; + struct v4l2_audio aud_input; + struct v4l2_control ctrl; + struct i2c_client *c; + memset(&aud_input, 0, sizeof(aud_input)); gpio_inout(bttv_tvcards[btv->c.type].gpiomask, bttv_tvcards[btv->c.type].gpiomask); signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC; - switch (mode) { - case AUDIO_MUTE: - btv->audio |= AUDIO_MUTE; - break; - case AUDIO_UNMUTE: - btv->audio &= ~AUDIO_MUTE; - break; - case AUDIO_TUNER: - case AUDIO_RADIO: - case AUDIO_EXTERN: - case AUDIO_INTERN: - btv->audio &= AUDIO_MUTE; - btv->audio |= mode; - } - i2c_mux = mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio; - if (btv->opt_automute && !signal && !btv->radio_user) - mux = AUDIO_OFF; - - val = bttv_tvcards[btv->c.type].audiomux[mux]; - gpio_bits(bttv_tvcards[btv->c.type].gpiomask,val); + btv->mute = mute; + btv->audio = input; + + /* automute */ + mute = mute || (btv->opt_automute && !signal && !btv->radio_user); + + if (mute) + gpio_val = bttv_tvcards[btv->c.type].gpiomute; + else + gpio_val = bttv_tvcards[btv->c.type].gpiomux[input]; + aud_input.index = btv->audio; + + gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val); if (bttv_gpio) - bttv_gpio_tracking(btv,audio_modes[mux]); - if (!in_interrupt()) - bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(i2c_mux)); + bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]); + if (in_interrupt()) + return 0; + + ctrl.id = V4L2_CID_AUDIO_MUTE; + /* take automute into account, just btv->mute is not enough */ + ctrl.value = mute; + bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, &ctrl); + c = btv->i2c_msp34xx_client; + if (c) + c->driver->command(c, VIDIOC_S_AUDIO, &aud_input); + c = btv->i2c_tvaudio_client; + if (c) + c->driver->command(c, VIDIOC_S_AUDIO, &aud_input); return 0; } +static inline int +audio_mute(struct bttv *btv, int mute) +{ + return audio_mux(btv, btv->audio, mute); +} + +static inline int +audio_input(struct bttv *btv, int input) +{ + return audio_mux(btv, input, btv->mute); +} + static void i2c_vidiocschan(struct bttv *btv) { @@ -1023,8 +1044,8 @@ set_input(struct bttv *btv, unsigned int input) } else { video_mux(btv,input); } - audio_mux(btv,(input == bttv_tvcards[btv->c.type].tuner ? - AUDIO_TUNER : AUDIO_EXTERN)); + audio_input(btv,(input == bttv_tvcards[btv->c.type].tuner ? + TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN)); set_tvnorm(btv,btv->tvnorm); i2c_vidiocschan(btv); } @@ -1236,10 +1257,10 @@ static int set_control(struct bttv *btv, struct v4l2_control *c) case V4L2_CID_AUDIO_MUTE: if (c->value) { va.flags |= VIDEO_AUDIO_MUTE; - audio_mux(btv, AUDIO_MUTE); + audio_mute(btv, 1); } else { va.flags &= ~VIDEO_AUDIO_MUTE; - audio_mux(btv, AUDIO_UNMUTE); + audio_mute(btv, 0); } break; @@ -1654,7 +1675,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return -EINVAL; mutex_lock(&btv->lock); - audio_mux(btv, (v->flags&VIDEO_AUDIO_MUTE) ? AUDIO_MUTE : AUDIO_UNMUTE); + audio_mute(btv, (v->flags&VIDEO_AUDIO_MUTE) ? 1 : 0); bttv_call_i2c_clients(btv,cmd,v); /* card specific hooks */ @@ -3163,8 +3184,8 @@ static int radio_open(struct inode *inode, struct file *file) file->private_data = btv; - bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type); - audio_mux(btv,AUDIO_RADIO); + bttv_call_i2c_clients(btv,AUDC_SET_RADIO,NULL); + audio_input(btv,TVAUDIO_INPUT_RADIO); mutex_unlock(&btv->lock); return 0; @@ -3750,7 +3771,7 @@ static irqreturn_t bttv_irq(int irq, void *dev_id, struct pt_regs * regs) bttv_irq_switch_video(btv); if ((astat & BT848_INT_HLOCK) && btv->opt_automute) - audio_mux(btv, -1); + audio_mute(btv, btv->mute); /* trigger automute */ if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) { printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->c.nr, @@ -4051,7 +4072,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, bt848_contrast(btv,32768); bt848_hue(btv,32768); bt848_sat(btv,32768); - audio_mux(btv,AUDIO_MUTE); + audio_mute(btv, 1); set_input(btv,0); } diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index 614c12018557..4b562b386fcf 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -302,6 +302,10 @@ static int attach_inform(struct i2c_client *client) if (!client->driver->command) return 0; + if (client->driver->id == I2C_DRIVERID_MSP3400) + btv->i2c_msp34xx_client = client; + if (client->driver->id == I2C_DRIVERID_TVAUDIO) + btv->i2c_tvaudio_client = client; if (btv->tuner_type != UNSET) { struct tuner_setup tun_setup; diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index ebde3e8219cf..3a23265c1538 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -234,7 +234,8 @@ struct tvcard unsigned int digital_mode; // DIGITAL_MODE_CAMERA or DIGITAL_MODE_VIDEO u32 gpiomask; u32 muxsel[16]; - u32 audiomux[6]; /* Tuner, Radio, external, internal, mute, stereo */ + u32 gpiomux[4]; /* Tuner, Radio, external, internal */ + u32 gpiomute; /* GPIO mute setting */ u32 gpiomask2; /* GPIO MUX mask */ /* i2c audio flags */ diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index 12223a203960..ee989d2e15d9 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -41,7 +41,6 @@ #include #include -#include #include #include #include @@ -299,6 +298,8 @@ struct bttv { int i2c_state, i2c_rc; int i2c_done; wait_queue_head_t i2c_queue; + struct i2c_client *i2c_msp34xx_client; + struct i2c_client *i2c_tvaudio_client; /* video4linux (1) */ struct video_device *video_dev; @@ -321,6 +322,7 @@ struct bttv { /* video state */ unsigned int input; unsigned int audio; + unsigned int mute; unsigned long freq; int tvnorm,hue,contrast,bright,saturation; struct v4l2_framebuffer fbuf; diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index 8739c64785ef..bc3331870240 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -75,11 +75,6 @@ static int cs53l32a_command(struct i2c_client *client, unsigned int cmd, cs53l32a_write(client, 0x01, 0x01 + (input->index << 4)); break; - case VIDIOC_G_AUDIO: - memset(input, 0, sizeof(*input)); - input->index = (cs53l32a_read(client, 0x01) >> 4) & 3; - break; - case VIDIOC_G_CTRL: if (ctrl->id == V4L2_CID_AUDIO_MUTE) { ctrl->value = (cs53l32a_read(client, 0x03) & 0xc0) != 0; diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c index cb9a7981e408..a4540e858f21 100644 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -18,7 +18,6 @@ #include #include -#include #include #include "cx25840.h" diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index bcb7ef85eb23..7aee37645d63 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include "cx25840.h" @@ -764,16 +763,6 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, return set_input(client, state->vid_input, input->index); } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *input = arg; - - memset(input, 0, sizeof(*input)); - input->index = state->aud_input; - input->capability = V4L2_AUDCAP_STEREO; - break; - } - case VIDIOC_S_FREQUENCY: input_change(client); break; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 5b2e499eab22..326a25f147f6 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -27,7 +27,6 @@ #include #include -#include #include #include diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 11ea9765769c..bd0b036c5cab 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include #include "msp3400.h" @@ -585,51 +585,12 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct msp_state *state = i2c_get_clientdata(client); - u16 *sarg = arg; int scart = 0; if (msp_debug >= 2) v4l_i2c_print_ioctl(client, cmd); switch (cmd) { - case AUDC_SET_INPUT: - if (*sarg == state->input) - break; - state->input = *sarg; - switch (*sarg) { - case AUDIO_RADIO: - /* Hauppauge uses IN2 for the radio */ - state->mode = MSP_MODE_FM_RADIO; - scart = SCART_IN2; - break; - case AUDIO_EXTERN_1: - /* IN1 is often used for external input ... */ - state->mode = MSP_MODE_EXTERN; - scart = SCART_IN1; - break; - case AUDIO_EXTERN_2: - /* ... sometimes it is IN2 through ;) */ - state->mode = MSP_MODE_EXTERN; - scart = SCART_IN2; - break; - case AUDIO_TUNER: - state->mode = -1; - break; - default: - if (*sarg & AUDIO_MUTE) - msp_set_scart(client, SCART_MUTE, 0); - break; - } - if (scart) { - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp_set_scart(client, scart, 0); - msp_write_dsp(client, 0x000d, 0x1900); - if (state->opmode != OPMODE_AUTOSELECT) - msp_set_audmode(client); - } - msp_wake_thread(client); - break; - case AUDC_SET_RADIO: if (state->radio) return 0; @@ -750,82 +711,27 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; - - if (i->index != 0) - return -EINVAL; - - i->type = V4L2_INPUT_TYPE_TUNER; - switch (i->index) { - case AUDIO_RADIO: - strcpy(i->name, "Radio"); - break; - case AUDIO_EXTERN_1: - strcpy(i->name, "Extern 1"); - break; - case AUDIO_EXTERN_2: - strcpy(i->name, "Extern 2"); - break; - case AUDIO_TUNER: - strcpy(i->name, "Television"); - break; - default: - return -EINVAL; - } - return 0; - } - - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; - - memset(a, 0, sizeof(*a)); - - switch (a->index) { - case AUDIO_RADIO: - strcpy(a->name, "Radio"); - break; - case AUDIO_EXTERN_1: - strcpy(a->name, "Extern 1"); - break; - case AUDIO_EXTERN_2: - strcpy(a->name, "Extern 2"); - break; - case AUDIO_TUNER: - strcpy(a->name, "Television"); - break; - default: - return -EINVAL; - } - - a->capability = V4L2_AUDCAP_STEREO; - a->mode = 0; /* TODO: add support for AVL */ - break; - } - case VIDIOC_S_AUDIO: { struct v4l2_audio *sarg = arg; switch (sarg->index) { - case AUDIO_RADIO: + case TVAUDIO_INPUT_RADIO: /* Hauppauge uses IN2 for the radio */ state->mode = MSP_MODE_FM_RADIO; scart = SCART_IN2; break; - case AUDIO_EXTERN_1: + case TVAUDIO_INPUT_EXTERN: /* IN1 is often used for external input ... */ state->mode = MSP_MODE_EXTERN; scart = SCART_IN1; break; - case AUDIO_EXTERN_2: + case TVAUDIO_INPUT_INTERN: /* ... sometimes it is IN2 through ;) */ state->mode = MSP_MODE_EXTERN; scart = SCART_IN2; break; - case AUDIO_TUNER: + case TVAUDIO_INPUT_TUNER: state->mode = -1; break; } diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index 852ab6a115fa..71a944b447e2 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include "msp3400.h" diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index b184fd00b4e7..b05015282601 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -40,7 +40,6 @@ #include #include #include -#include #include MODULE_DESCRIPTION("Philips SAA7113/SAA7114/SAA7115 video decoder driver"); diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 104bd2e054e5..31ba293854c1 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -34,7 +34,6 @@ #include #include -#include #include #include #include diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index 12b8981efd13..78e043ac9ea0 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -48,7 +48,6 @@ #include #include -#include #include #include diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index ccfd3b90ea9f..103ccb919292 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -30,7 +30,6 @@ #include #include -#include #include @@ -47,7 +46,6 @@ I2C_CLIENT_INSMOD; /* This is a superset of the TDA9875 */ struct tda9875 { - int mode; int rvol, lvol; int bass, treble; struct i2c_client c; @@ -197,7 +195,6 @@ static void do_tda9875_init(struct i2c_client *client) tda9875_write(client, TDA9875_MUT, 0xcc ); /* General mute */ - t->mode=AUDIO_UNMUTE; t->lvol=t->rvol =0; /* 0dB */ t->bass=0; /* 0dB */ t->treble=0; /* 0dB */ diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 32e1849441fb..df195c905366 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -21,7 +21,6 @@ #include #include -#include #define UNSET (-1U) diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 4cc0497dcbde..15fd55ff69ca 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include @@ -102,7 +102,7 @@ struct CHIPDESC { /* input switch register + values for v4l inputs */ int inputreg; - int inputmap[8]; + int inputmap[4]; int inputmute; int inputmask; }; @@ -119,9 +119,10 @@ struct CHIPSTATE { audiocmd shadow; /* current settings */ - __u16 left,right,treble,bass,mode; + __u16 left,right,treble,bass,muted,mode; int prevmode; int radio; + int input; /* thread */ pid_t tpid; @@ -1101,9 +1102,8 @@ static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } static int tda8425_initialize(struct CHIPSTATE *chip) { struct CHIPDESC *desc = chiplist + chip->type; - int inputmap[8] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1, - /* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF, - /* off */ TDA8425_S1_OFF, /* on */ TDA8425_S1_CH2}; + int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1, + /* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF}; if (chip->c.adapter->id == I2C_HW_B_RIVA) { memcpy (desc->inputmap, inputmap, sizeof (inputmap)); @@ -1298,7 +1298,7 @@ static struct CHIPDESC chiplist[] = { .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, .inputreg = TDA9873_SW, .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE, - .inputmap = {0xa0, 0xa2, 0xa0, 0xa0, 0xc0}, + .inputmap = {0xa0, 0xa2, 0xa0, 0xa0}, .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE, }, @@ -1446,8 +1446,7 @@ static struct CHIPDESC chiplist[] = { .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER, PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, - PIC16C54_MISC_SND_MUTE,PIC16C54_MISC_SND_MUTE, - PIC16C54_MISC_SND_NOTMUTE}, + PIC16C54_MISC_SND_MUTE}, .inputmute = PIC16C54_MISC_SND_MUTE, }, { @@ -1583,28 +1582,40 @@ static int chip_detach(struct i2c_client *client) return 0; } +static int tvaudio_set_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl) +{ + struct CHIPDESC *desc = chiplist + chip->type; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value < 0 || ctrl->value >= 2) + return -ERANGE; + chip->muted = ctrl->value; + if (chip->muted) + chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); + else + chip_write_masked(chip,desc->inputreg, + desc->inputmap[chip->input],desc->inputmask); + break; + default: + return -EINVAL; + } + return 0; +} + + /* ---------------------------------------------------------------------- */ /* video4linux interface */ static int chip_command(struct i2c_client *client, unsigned int cmd, void *arg) { - __u16 *sarg = arg; struct CHIPSTATE *chip = i2c_get_clientdata(client); struct CHIPDESC *desc = chiplist + chip->type; v4l_dbg(1, debug, &chip->c, "%s: chip_command 0x%x\n", chip->c.name, cmd); switch (cmd) { - case AUDC_SET_INPUT: - if (desc->flags & CHIP_HAS_INPUTSEL) { - if (*sarg & 0x80) - chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); - else - chip_write_masked(chip,desc->inputreg,desc->inputmap[*sarg],desc->inputmask); - } - break; - case AUDC_SET_RADIO: chip->radio = 1; chip->watch_stereo = 0; @@ -1668,6 +1679,24 @@ static int chip_command(struct i2c_client *client, break; } + case VIDIOC_S_CTRL: + return tvaudio_set_ctrl(chip, arg); + + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *sarg = arg; + + if (!(desc->flags & CHIP_HAS_INPUTSEL) || sarg->index >= 4) + return -EINVAL; + /* There are four inputs: tuner, radio, extern and intern. */ + chip->input = sarg->index; + if (chip->muted) + break; + chip_write_masked(chip, desc->inputreg, + desc->inputmap[chip->input], desc->inputmask); + break; + } + case VIDIOC_S_TUNER: { struct v4l2_tuner *vt = arg; diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 582551b0969b..e0d2ff83fc91 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -248,32 +248,32 @@ audioIC[] = {AUDIO_CHIP_MSP34XX, "MSP3410D"}, {AUDIO_CHIP_MSP34XX, "MSP3415"}, {AUDIO_CHIP_MSP34XX, "MSP3430"}, - {AUDIO_CHIP_UNKNOWN, "MSP3438"}, + {AUDIO_CHIP_MSP34XX, "MSP3438"}, {AUDIO_CHIP_UNKNOWN, "CS5331"}, /* 10-14 */ {AUDIO_CHIP_MSP34XX, "MSP3435"}, {AUDIO_CHIP_MSP34XX, "MSP3440"}, {AUDIO_CHIP_MSP34XX, "MSP3445"}, - {AUDIO_CHIP_UNKNOWN, "MSP3411"}, - {AUDIO_CHIP_UNKNOWN, "MSP3416"}, + {AUDIO_CHIP_MSP34XX, "MSP3411"}, + {AUDIO_CHIP_MSP34XX, "MSP3416"}, /* 15-19 */ {AUDIO_CHIP_MSP34XX, "MSP3425"}, - {AUDIO_CHIP_UNKNOWN, "MSP3451"}, - {AUDIO_CHIP_UNKNOWN, "MSP3418"}, + {AUDIO_CHIP_MSP34XX, "MSP3451"}, + {AUDIO_CHIP_MSP34XX, "MSP3418"}, {AUDIO_CHIP_UNKNOWN, "Type 0x12"}, {AUDIO_CHIP_UNKNOWN, "OKI7716"}, /* 20-24 */ - {AUDIO_CHIP_UNKNOWN, "MSP4410"}, - {AUDIO_CHIP_UNKNOWN, "MSP4420"}, - {AUDIO_CHIP_UNKNOWN, "MSP4440"}, - {AUDIO_CHIP_UNKNOWN, "MSP4450"}, - {AUDIO_CHIP_UNKNOWN, "MSP4408"}, + {AUDIO_CHIP_MSP34XX, "MSP4410"}, + {AUDIO_CHIP_MSP34XX, "MSP4420"}, + {AUDIO_CHIP_MSP34XX, "MSP4440"}, + {AUDIO_CHIP_MSP34XX, "MSP4450"}, + {AUDIO_CHIP_MSP34XX, "MSP4408"}, /* 25-29 */ - {AUDIO_CHIP_UNKNOWN, "MSP4418"}, - {AUDIO_CHIP_UNKNOWN, "MSP4428"}, - {AUDIO_CHIP_UNKNOWN, "MSP4448"}, - {AUDIO_CHIP_UNKNOWN, "MSP4458"}, - {AUDIO_CHIP_UNKNOWN, "Type 0x1d"}, + {AUDIO_CHIP_MSP34XX, "MSP4418"}, + {AUDIO_CHIP_MSP34XX, "MSP4428"}, + {AUDIO_CHIP_MSP34XX, "MSP4448"}, + {AUDIO_CHIP_MSP34XX, "MSP4458"}, + {AUDIO_CHIP_MSP34XX, "Type 0x1d"}, /* 30-34 */ {AUDIO_CHIP_INTERNAL, "CX880"}, {AUDIO_CHIP_INTERNAL, "CX881"}, diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 1717663d3e51..d1234d781e16 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -312,7 +312,6 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(DECODER_DUMP)] = "DECODER_DUMP", #endif [_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO", - [_IOC_NR(AUDC_SET_INPUT)] = "AUDC_SET_INPUT", [_IOC_NR(MSP_SET_MATRIX)] = "MSP_SET_MATRIX", [_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR", @@ -419,7 +418,6 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) case TUNER_SET_TYPE_ADDR: case TUNER_SET_STANDBY: case TDA9887_SET_CONFIG: - case AUDC_SET_INPUT: case VIDIOC_OVERLAY_OLD: case VIDIOC_STREAMOFF: case VIDIOC_G_OUTPUT: diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 8cb64f8a8a91..9b90225226e6 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -102,11 +102,6 @@ static int wm8775_command(struct i2c_client *client, unsigned int cmd, wm8775_write(client, R21, 0x100 + state->input); break; - case VIDIOC_G_AUDIO: - memset(input, 0, sizeof(*input)); - input->index = state->input; - break; - case VIDIOC_G_CTRL: if (ctrl->id != V4L2_CID_AUDIO_MUTE) return -EINVAL; diff --git a/include/media/audiochip.h b/include/media/audiochip.h index 295d256ee811..1fd4a2207574 100644 --- a/include/media/audiochip.h +++ b/include/media/audiochip.h @@ -21,18 +21,4 @@ enum audiochip { AUDIO_CHIP_MSP34XX }; -/* ---------------------------------------------------------------------- */ - -/* audio inputs */ -#define AUDIO_TUNER 0x00 -#define AUDIO_RADIO 0x01 -#define AUDIO_EXTERN 0x02 -#define AUDIO_INTERN 0x03 -#define AUDIO_OFF 0x04 -#define AUDIO_ON 0x05 -#define AUDIO_EXTERN_1 AUDIO_EXTERN -#define AUDIO_EXTERN_2 0x06 -#define AUDIO_MUTE 0x80 -#define AUDIO_UNMUTE 0x81 - #endif /* AUDIOCHIP_H */ diff --git a/include/media/tvaudio.h b/include/media/tvaudio.h new file mode 100644 index 000000000000..6915aafc875a --- /dev/null +++ b/include/media/tvaudio.h @@ -0,0 +1,30 @@ +/* + tvaudio.h - definition for tvaudio inputs + + Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl) + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _TVAUDIO_H +#define _TVAUDIO_H + +/* The tvaudio module accepts the following inputs: */ +#define TVAUDIO_INPUT_TUNER 0 +#define TVAUDIO_INPUT_RADIO 1 +#define TVAUDIO_INPUT_EXTERN 2 +#define TVAUDIO_INPUT_INTERN 3 + +#endif diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 2360453e7496..07130474a0df 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -123,9 +123,6 @@ enum v4l2_chip_ident { /* v4l device was opened in Radio mode, to be replaced by VIDIOC_INT_S_TUNER_MODE */ #define AUDC_SET_RADIO _IO('d',88) -/* select from TV,radio,extern,MUTE, to be replaced with VIDIOC_INT_S_AUDIO_ROUTING */ -#define AUDC_SET_INPUT _IOW('d',89,int) - /* msp3400 ioctl: will be removed in the near future, to be replaced by VIDIOC_INT_S_AUDIO_ROUTING. */ struct msp_matrix { @@ -209,10 +206,10 @@ struct v4l2_routing { }; /* These internal commands should be used to define the inputs and outputs - of an audio/video chip. They will replace AUDC_SET_INPUT. - The v4l2 API commands VIDIOC_S/G_INPUT, VIDIOC_S/G_OUTPUT, - VIDIOC_S/G_AUDIO and VIDIOC_S/G_AUDOUT are meant to be used by the - user. Internally these commands should be used to switch inputs/outputs + of an audio/video chip. They will replace the v4l2 API commands + VIDIOC_S/G_INPUT, VIDIOC_S/G_OUTPUT, VIDIOC_S/G_AUDIO and VIDIOC_S/G_AUDOUT + that are meant to be used by the user. + The internal commands should be used to switch inputs/outputs because only the driver knows how to map a 'Television' input to the precise input/output routing of an A/D converter, or a DSP, or a video digitizer. These four commands should only be sent directly to an i2c device, they -- cgit v1.2.3 From 49965a80a4c4f5cbe15fb3bb1f8f8b0ec4ef02bc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 19 Mar 2006 08:45:38 -0300 Subject: V4L/DVB (3581): Add new media/msp3400.h header containing the routing macros Moved msp3400.h to msp3400-driver.h. Created media/msp3400.h with the new routing defines and lots of comments. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 1 - drivers/media/video/msp3400-driver.c | 3 +- drivers/media/video/msp3400-driver.h | 113 +++++++++++++++ drivers/media/video/msp3400-kthreads.c | 3 +- drivers/media/video/msp3400.h | 113 --------------- include/media/msp3400.h | 221 ++++++++++++++++++++++++++++++ 6 files changed, 338 insertions(+), 116 deletions(-) create mode 100644 drivers/media/video/msp3400-driver.h delete mode 100644 drivers/media/video/msp3400.h create mode 100644 include/media/msp3400.h (limited to 'include') diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 4e22fc4889e1..2d68a27417eb 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -31,7 +31,6 @@ #include #include #include -#include "msp3400.h" #include "em28xx.h" diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 9a47ba22c133..04250284ff1e 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -54,9 +54,10 @@ #include #include #include +#include #include #include -#include "msp3400.h" +#include "msp3400-driver.h" /* ---------------------------------------------------------------------- */ diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h new file mode 100644 index 000000000000..04821ebfe04a --- /dev/null +++ b/drivers/media/video/msp3400-driver.h @@ -0,0 +1,113 @@ +/* + */ + +#ifndef MSP3400_DRIVER_H +#define MSP3400_DRIVER_H + +/* ---------------------------------------------------------------------- */ + +/* This macro is allowed for *constants* only, gcc must calculate it + at compile time. Remember -- no floats in kernel mode */ +#define MSP_CARRIER(freq) ((int)((float)(freq / 18.432) * (1 << 24))) + +#define MSP_MODE_AM_DETECT 0 +#define MSP_MODE_FM_RADIO 2 +#define MSP_MODE_FM_TERRA 3 +#define MSP_MODE_FM_SAT 4 +#define MSP_MODE_FM_NICAM1 5 +#define MSP_MODE_FM_NICAM2 6 +#define MSP_MODE_AM_NICAM 7 +#define MSP_MODE_BTSC 8 +#define MSP_MODE_EXTERN 9 + +#define SCART_IN1 0 +#define SCART_IN2 1 +#define SCART_IN3 2 +#define SCART_IN4 3 +#define SCART_IN1_DA 4 +#define SCART_IN2_DA 5 +#define SCART_MONO 6 +#define SCART_MUTE 7 + +#define SCART_DSP_IN 0 +#define SCART1_OUT 1 +#define SCART2_OUT 2 + +#define OPMODE_AUTO -1 +#define OPMODE_MANUAL 0 +#define OPMODE_AUTODETECT 1 /* use autodetect (>= msp3410 only) */ +#define OPMODE_AUTOSELECT 2 /* use autodetect & autoselect (>= msp34xxG) */ + +/* module parameters */ +extern int msp_debug; +extern int msp_once; +extern int msp_amsound; +extern int msp_standard; +extern int msp_dolby; +extern int msp_stereo_thresh; + +struct msp_state { + int rev1, rev2; + u8 has_nicam; + u8 has_radio; + u8 has_headphones; + u8 has_ntsc_jp_d_k3; + u8 has_scart4; + u8 has_scart23_in_scart2_out; + u8 has_scart2_out_volume; + u8 has_i2s_conf; + u8 has_subwoofer; + u8 has_sound_processing; + u8 has_virtual_dolby_surround; + u8 has_dolby_pro_logic; + + int radio; + int opmode; + int std; + int mode; + v4l2_std_id v4l2_std; + int nicam_on; + int acb; + int in_scart; + int i2s_mode; + int main, second; /* sound carrier */ + int input; + int source; /* see msp34xxg_set_source */ + + /* v4l2 */ + int audmode; + int rxsubchans; + + int volume, muted; + int balance, loudness; + int bass, treble; + + /* thread */ + struct task_struct *kthread; + wait_queue_head_t wq; + int restart:1; + int watch_stereo:1; +}; + +/* msp3400-driver.c */ +int msp_write_dem(struct i2c_client *client, int addr, int val); +int msp_write_dsp(struct i2c_client *client, int addr, int val); +int msp_read_dem(struct i2c_client *client, int addr); +int msp_read_dsp(struct i2c_client *client, int addr); +int msp_reset(struct i2c_client *client); +void msp_set_scart(struct i2c_client *client, int in, int out); +void msp_set_mute(struct i2c_client *client); +void msp_set_audio(struct i2c_client *client); +int msp_sleep(struct msp_state *state, int timeout); + +/* msp3400-kthreads.c */ +const char *msp_standard_std_name(int std); +void msp_set_audmode(struct i2c_client *client); +int msp_detect_stereo(struct i2c_client *client); +int msp3400c_thread(void *data); +int msp3410d_thread(void *data); +int msp34xxg_thread(void *data); +void msp3400c_set_mode(struct i2c_client *client, int mode); +void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2); + +#endif /* MSP3400_DRIVER_H */ diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index 88b216cc49d7..9ee8dc216d7f 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -26,9 +26,10 @@ #include #include #include +#include #include #include -#include "msp3400.h" +#include "msp3400-driver.h" /* this one uses the automatic sound standard detection of newer msp34xx chip versions */ diff --git a/drivers/media/video/msp3400.h b/drivers/media/video/msp3400.h deleted file mode 100644 index 25482046fc68..000000000000 --- a/drivers/media/video/msp3400.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - */ - -#ifndef MSP3400_H -#define MSP3400_H - -/* ---------------------------------------------------------------------- */ - -/* This macro is allowed for *constants* only, gcc must calculate it - at compile time. Remember -- no floats in kernel mode */ -#define MSP_CARRIER(freq) ((int)((float)(freq / 18.432) * (1 << 24))) - -#define MSP_MODE_AM_DETECT 0 -#define MSP_MODE_FM_RADIO 2 -#define MSP_MODE_FM_TERRA 3 -#define MSP_MODE_FM_SAT 4 -#define MSP_MODE_FM_NICAM1 5 -#define MSP_MODE_FM_NICAM2 6 -#define MSP_MODE_AM_NICAM 7 -#define MSP_MODE_BTSC 8 -#define MSP_MODE_EXTERN 9 - -#define SCART_IN1 0 -#define SCART_IN2 1 -#define SCART_IN3 2 -#define SCART_IN4 3 -#define SCART_IN1_DA 4 -#define SCART_IN2_DA 5 -#define SCART_MONO 6 -#define SCART_MUTE 7 - -#define SCART_DSP_IN 0 -#define SCART1_OUT 1 -#define SCART2_OUT 2 - -#define OPMODE_AUTO -1 -#define OPMODE_MANUAL 0 -#define OPMODE_AUTODETECT 1 /* use autodetect (>= msp3410 only) */ -#define OPMODE_AUTOSELECT 2 /* use autodetect & autoselect (>= msp34xxG) */ - -/* module parameters */ -extern int msp_debug; -extern int msp_once; -extern int msp_amsound; -extern int msp_standard; -extern int msp_dolby; -extern int msp_stereo_thresh; - -struct msp_state { - int rev1, rev2; - u8 has_nicam; - u8 has_radio; - u8 has_headphones; - u8 has_ntsc_jp_d_k3; - u8 has_scart4; - u8 has_scart23_in_scart2_out; - u8 has_scart2_out_volume; - u8 has_i2s_conf; - u8 has_subwoofer; - u8 has_sound_processing; - u8 has_virtual_dolby_surround; - u8 has_dolby_pro_logic; - - int radio; - int opmode; - int std; - int mode; - v4l2_std_id v4l2_std; - int nicam_on; - int acb; - int in_scart; - int i2s_mode; - int main, second; /* sound carrier */ - int input; - int source; /* see msp34xxg_set_source */ - - /* v4l2 */ - int audmode; - int rxsubchans; - - int volume, muted; - int balance, loudness; - int bass, treble; - - /* thread */ - struct task_struct *kthread; - wait_queue_head_t wq; - int restart:1; - int watch_stereo:1; -}; - -/* msp3400-driver.c */ -int msp_write_dem(struct i2c_client *client, int addr, int val); -int msp_write_dsp(struct i2c_client *client, int addr, int val); -int msp_read_dem(struct i2c_client *client, int addr); -int msp_read_dsp(struct i2c_client *client, int addr); -int msp_reset(struct i2c_client *client); -void msp_set_scart(struct i2c_client *client, int in, int out); -void msp_set_mute(struct i2c_client *client); -void msp_set_audio(struct i2c_client *client); -int msp_sleep(struct msp_state *state, int timeout); - -/* msp3400-kthreads.c */ -const char *msp_standard_std_name(int std); -void msp_set_audmode(struct i2c_client *client); -int msp_detect_stereo(struct i2c_client *client); -int msp3400c_thread(void *data); -int msp3410d_thread(void *data); -int msp34xxg_thread(void *data); -void msp3400c_set_mode(struct i2c_client *client, int mode); -void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2); - -#endif /* MSP3400_H */ diff --git a/include/media/msp3400.h b/include/media/msp3400.h new file mode 100644 index 000000000000..26e26585273f --- /dev/null +++ b/include/media/msp3400.h @@ -0,0 +1,221 @@ +/* + msp3400.h - definition for msp3400 inputs and outputs + + Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl) + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _MSP3400_H_ +#define _MSP3400_H_ + +/* msp3400 routing + =============== + + The msp3400 has a complicated routing scheme with many possible + combinations. The details are all in the datasheets but I will try + to give a short description here. + + Inputs + ====== + + There are 1) tuner inputs, 2) I2S inputs, 3) SCART inputs. You will have + to select which tuner input to use and which SCART input to use. The + selected tuner input, the selected SCART input and all I2S inputs go to + the DSP (the tuner input first goes through the demodulator). + + The DSP handles things like volume, bass/treble, balance, and some chips + have support for surround sound. It has several outputs: MAIN, AUX, I2S + and SCART1/2. Each output can select which DSP input to use. So the MAIN + output can select the tuner input while at the same time the SCART1 output + uses the I2S input. + + Outputs + ======= + + Most DSP outputs are also the outputs of the msp3400. However, the SCART + outputs of the msp3400 can select which input to use: either the SCART1 or + SCART2 output from the DSP, or the msp3400 SCART inputs, thus completely + bypassing the DSP. + + Summary + ======= + + So to specify a complete routing scheme for the msp3400 you will have to + specify in the 'input' field of the v4l2_routing struct: + + 1) which tuner input to use + 2) which SCART input to use + 3) which DSP input to use for each DSP output + + And in the 'output' field of the v4l2_routing struct you specify: + + 1) which SCART input to use for each SCART output + + Depending on how the msp is wired to the other components you can + ignore or mute certain inputs or outputs. + + Also, depending on the msp version only a subset of the inputs or + outputs may be present. At the end of this header some tables are + added containing a list of what is available for each msp version. + */ + +/* Inputs to the DSP unit: two independent selections have to be made: + 1) the tuner (SIF) input + 2) the SCART input + Bits 0-2 are used for the SCART input select, bit 3 is used for the tuner + input, bits 4-7 are reserved. + */ + +/* SCART input to DSP selection */ +#define MSP_IN_SCART_1 0 /* Pin SC1_IN */ +#define MSP_IN_SCART_2 1 /* Pin SC2_IN */ +#define MSP_IN_SCART_3 2 /* Pin SC3_IN */ +#define MSP_IN_SCART_4 3 /* Pin SC4_IN */ +#define MSP_IN_MONO 6 /* Pin MONO_IN */ +#define MSP_IN_MUTE 7 /* Mute DSP input */ +#define MSP_SCART_TO_DSP(in) (in) +/* Tuner input to demodulator and DSP selection */ +#define MSP_IN_TUNER_1 0 /* Analog Sound IF input pin ANA_IN1 */ +#define MSP_IN_TUNER_2 1 /* Analog Sound IF input pin ANA_IN2 */ +#define MSP_TUNER_TO_DSP(in) ((in) << 3) + +/* The msp has up to 5 DSP outputs, each output can independently select + a DSP input. + + The DSP outputs are: loudspeaker output (aka MAIN), headphones output + (aka AUX), SCART1 DA output, SCART2 DA output and an I2S output. + There also is a quasi-peak detector output, but that is not used by + this driver and is set to the same input as the loudspeaker output. + Not all outputs are supported by all msp models. Setting the input + of an unsupported output will be ignored by the driver. + + There are up to 16 DSP inputs to choose from, so each output is + assigned 4 bits. + + Note: the 44x8G can mix two inputs and feed the result back to the + DSP. This is currently not implemented. Also not implemented is the + multi-channel capable I2S3 input of the 44x0G. If someone can demonstrate + a need for one of those features then additional support can be added. */ +#define MSP_DSP_OUT_TUNER 0 /* Tuner output */ +#define MSP_DSP_OUT_SCART 2 /* SCART output */ +#define MSP_DSP_OUT_I2S1 5 /* I2S1 output */ +#define MSP_DSP_OUT_I2S2 6 /* I2S2 output */ +#define MSP_DSP_OUT_I2S3 7 /* I2S3 output */ +#define MSP_DSP_OUT_MAIN_AVC 11 /* MAIN AVC processed output */ +#define MSP_DSP_OUT_MAIN 12 /* MAIN output */ +#define MSP_DSP_OUT_AUX 13 /* AUX output */ +#define MSP_DSP_TO_MAIN(in) ((in) << 4) +#define MSP_DSP_TO_AUX(in) ((in) << 8) +#define MSP_DSP_TO_SCART1(in) ((in) << 12) +#define MSP_DSP_TO_SCART2(in) ((in) << 16) +#define MSP_DSP_TO_I2S(in) ((in) << 20) + +/* Output SCART select: the SCART outputs can select which input + to use. */ +#define MSP_OUT_SCART1 0 /* SCART1 input, bypassing the DSP */ +#define MSP_OUT_SCART2 1 /* SCART2 input, bypassing the DSP */ +#define MSP_OUT_SCART3 2 /* SCART3 input, bypassing the DSP */ +#define MSP_OUT_SCART4 3 /* SCART4 input, bypassing the DSP */ +#define MSP_OUT_SCART1_DA 4 /* DSP SCART1 output */ +#define MSP_OUT_SCART2_DA 5 /* DSP SCART2 output */ +#define MSP_OUT_MONO 6 /* MONO input, bypassing the DSP */ +#define MSP_OUT_MUTE 7 /* MUTE output */ +#define MSP_OUT_TO_SCART1(in) (in) +#define MSP_OUT_TO_SCART2(in) ((in) << 4) + +/* Shortcut macros */ +#define MSP_INPUT(sc, t, main_aux_src, sc_i2s_src) \ + (MSP_SCART_TO_DSP(sc) | \ + MSP_TUNER_TO_DSP(t) | \ + MSP_DSP_TO_MAIN(main_aux_src) | \ + MSP_DSP_TO_AUX(main_aux_src) | \ + MSP_DSP_TO_SCART1(sc_i2s_src) | \ + MSP_DSP_TO_SCART2(sc_i2s_src) | \ + MSP_DSP_TO_I2S(sc_i2s_src)) +#define MSP_OUTPUT(sc) \ + (MSP_OUT_TO_SCART1(sc) | \ + MSP_OUT_TO_SCART2(sc)) + +/* Tuner inputs vs. msp version */ +/* Chip TUNER_1 TUNER_2 + ------------------------- + msp34x0b y y + msp34x0c y y + msp34x0d y y + msp34x5d y n + msp34x7d y n + msp34x0g y y + msp34x1g y y + msp34x2g y y + msp34x5g y n + msp34x7g y n + msp44x0g y y + msp44x8g y y + */ + +/* SCART inputs vs. msp version */ +/* Chip SC1 SC2 SC3 SC4 + ------------------------- + msp34x0b y y y n + msp34x0c y y y n + msp34x0d y y y y + msp34x5d y y n n + msp34x7d y n n n + msp34x0g y y y y + msp34x1g y y y y + msp34x2g y y y y + msp34x5g y y n n + msp34x7g y n n n + msp44x0g y y y y + msp44x8g y y y y + */ + +/* DSP inputs vs. msp version (tuner and SCART inputs are always available) */ +/* Chip I2S1 I2S2 I2S3 MAIN_AVC MAIN AUX + ------------------------------------------ + msp34x0b y n n n n n + msp34x0c y y n n n n + msp34x0d y y n n n n + msp34x5d y y n n n n + msp34x7d n n n n n n + msp34x0g y y n n n n + msp34x1g y y n n n n + msp34x2g y y n y y y + msp34x5g y y n n n n + msp34x7g n n n n n n + msp44x0g y y y y y y + msp44x8g y y y n n n + */ + +/* DSP outputs vs. msp version */ +/* Chip MAIN AUX SCART1 SCART2 I2S + ------------------------------------ + msp34x0b y y y n y + msp34x0c y y y n y + msp34x0d y y y y y + msp34x5d y n y n y + msp34x7d y n y n n + msp34x0g y y y y y + msp34x1g y y y y y + msp34x2g y y y y y + msp34x5g y n y n y + msp34x7g y n y n n + msp44x0g y y y y y + msp44x8g y y y y y + */ + +#endif /* MSP3400_H */ + -- cgit v1.2.3 From 2474ed444b475614ef795523076be7cc8437ae00 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 19 Mar 2006 12:35:57 -0300 Subject: V4L/DVB (3582): Implement correct msp3400 input/output routing - implement VIDIOC_INT_S_AUDIO_ROUTING for msp3400 and tvaudio - use the new command in bttv, pvrusb2 and em28xx. - remove the now obsolete MSP_SET_MATRIX from msp3400 (yeah!) - remove the obsolete VIDIOC_S_AUDIO from msp3400. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/bttv-driver.c | 52 +++++++++++--- drivers/media/video/em28xx/em28xx-cards.c | 8 ++- drivers/media/video/em28xx/em28xx-video.c | 8 ++- drivers/media/video/msp3400-driver.c | 67 +++++++++--------- drivers/media/video/msp3400-driver.h | 4 +- drivers/media/video/msp3400-kthreads.c | 111 ++++++++++++++++-------------- drivers/media/video/tvaudio.c | 24 +++++++ drivers/media/video/v4l2-common.c | 13 +--- include/media/msp3400.h | 5 ++ include/media/v4l2-common.h | 8 --- 10 files changed, 181 insertions(+), 119 deletions(-) (limited to 'include') diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index be567ec9e145..80e4a7406ac2 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -37,6 +37,7 @@ #include "bttvp.h" #include #include +#include #include @@ -934,11 +935,9 @@ static int audio_mux(struct bttv *btv, int input, int mute) { int gpio_val, signal; - struct v4l2_audio aud_input; struct v4l2_control ctrl; struct i2c_client *c; - memset(&aud_input, 0, sizeof(aud_input)); gpio_inout(bttv_tvcards[btv->c.type].gpiomask, bttv_tvcards[btv->c.type].gpiomask); signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC; @@ -953,7 +952,6 @@ audio_mux(struct bttv *btv, int input, int mute) gpio_val = bttv_tvcards[btv->c.type].gpiomute; else gpio_val = bttv_tvcards[btv->c.type].gpiomux[input]; - aud_input.index = btv->audio; gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val); if (bttv_gpio) @@ -962,15 +960,51 @@ audio_mux(struct bttv *btv, int input, int mute) return 0; ctrl.id = V4L2_CID_AUDIO_MUTE; - /* take automute into account, just btv->mute is not enough */ - ctrl.value = mute; + ctrl.value = btv->mute; bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, &ctrl); c = btv->i2c_msp34xx_client; - if (c) - c->driver->command(c, VIDIOC_S_AUDIO, &aud_input); + if (c) { + struct v4l2_routing route; + + /* Note: the inputs tuner/radio/extern/intern are translated + to msp routings. This assumes common behavior for all msp3400 + based TV cards. When this assumption fails, then the + specific MSP routing must be added to the card table. + For now this is sufficient. */ + switch (input) { + case TVAUDIO_INPUT_RADIO: + route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1, + MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + break; + case TVAUDIO_INPUT_EXTERN: + route.input = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, + MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + break; + case TVAUDIO_INPUT_INTERN: + /* Yes, this is the same input as for RADIO. I doubt + if this is ever used. The only board with an INTERN + input is the BTTV_BOARD_AVERMEDIA98. I wonder how + that was tested. My guess is that the whole INTERN + input does not work. */ + route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1, + MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + break; + case TVAUDIO_INPUT_TUNER: + default: + route.input = MSP_INPUT_DEFAULT; + break; + } + route.output = MSP_OUTPUT_DEFAULT; + c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route); + } c = btv->i2c_tvaudio_client; - if (c) - c->driver->command(c, VIDIOC_S_AUDIO, &aud_input); + if (c) { + struct v4l2_routing route; + + route.input = input; + route.output = 0; + c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route); + } return 0; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 2d68a27417eb..f62fd706b45a 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -28,8 +28,9 @@ #include #include #include -#include +#include #include +#include #include #include "em28xx.h" @@ -146,11 +147,12 @@ struct em28xx_board em28xx_boards[] = { .input = {{ .type = EM28XX_VMUX_TELEVISION, .vmux = 0, - .amux = 6, + .amux = MSP_INPUT_DEFAULT, },{ .type = EM28XX_VMUX_SVIDEO, .vmux = 2, - .amux = 1, + .amux = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, + MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART), }}, }, [EM2820_BOARD_MSI_VOX_USB_2] = { diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 780342f7b239..dfba33d0fa61 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -38,6 +38,7 @@ #include "em28xx.h" #include #include +#include #define DRIVER_AUTHOR "Ludovico Cavedon , " \ "Markus Rechberger , " \ @@ -216,9 +217,14 @@ static void video_mux(struct em28xx *dev, int index) em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,input,dev->ctl_ainput); if (dev->has_msp34xx) { + struct v4l2_routing route; + if (dev->i2s_speed) em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); - em28xx_i2c_call_clients(dev, VIDIOC_S_AUDIO, &dev->ctl_ainput); + route.input = dev->ctl_ainput; + route.output = MSP_OUTPUT(MSP_OUT_SCART1_DA); + /* Note: this is msp3400 specific */ + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route); ainput = EM28XX_AUDIO_SRC_TUNER; em28xx_audio_source(dev, ainput); } else { diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 04250284ff1e..8ba1c960388a 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -555,7 +555,6 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct msp_state *state = i2c_get_clientdata(client); - int scart = -1; if (msp_debug >= 2) v4l_i2c_print_ioctl(client, cmd); @@ -660,15 +659,6 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; } - /* msp34xx specific */ - case MSP_SET_MATRIX: - { - struct msp_matrix *mspm = arg; - - msp_set_scart(client, mspm->input - 1, mspm->output); - break; - } - /* --- v4l2 ioctls --- */ case VIDIOC_S_STD: { @@ -682,36 +672,38 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } - case VIDIOC_S_AUDIO: + case VIDIOC_INT_G_AUDIO_ROUTING: { - struct v4l2_audio *sarg = arg; + struct v4l2_routing *rt = arg; - switch (sarg->index) { - case TVAUDIO_INPUT_RADIO: - /* Hauppauge uses IN2 for the radio */ - state->mode = MSP_MODE_FM_RADIO; - scart = SCART_IN2; - break; - case TVAUDIO_INPUT_EXTERN: - /* IN1 is often used for external input ... */ - state->mode = MSP_MODE_EXTERN; - scart = SCART_IN1; - break; - case TVAUDIO_INPUT_INTERN: - /* ... sometimes it is IN2 through ;) */ - state->mode = MSP_MODE_EXTERN; - scart = SCART_IN2; - break; - case TVAUDIO_INPUT_TUNER: - state->mode = -1; - break; - } - if (scart >= 0) { - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp_set_scart(client, scart, 0); + *rt = state->routing; + break; + } + + case VIDIOC_INT_S_AUDIO_ROUTING: + { + struct v4l2_routing *rt = arg; + int tuner = (rt->input >> 3) & 1; + int old_tuner = (state->routing.input >> 3) & 1; + int sc_in = rt->input & 0x7; + int sc1_out = rt->output & 0xf; + int sc2_out = (rt->output >> 4) & 0xf; + u16 val; + + state->routing = *rt; + if (state->opmode == OPMODE_AUTOSELECT) { + val = msp_read_dem(client, 0x30) & ~0x100; + msp_write_dem(client, 0x30, val | (tuner ? 0x100 : 0)); + } else { + val = msp_read_dem(client, 0xbb) & ~0x100; + msp_write_dem(client, 0xbb, val | (tuner ? 0x100 : 0)); } + msp_set_scart(client, sc_in, 0); + msp_set_scart(client, sc1_out, 1); + msp_set_scart(client, sc2_out, 2); msp_set_audmode(client); - msp_wake_thread(client); + if (tuner != old_tuner) + msp_wake_thread(client); break; } @@ -941,6 +933,9 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) state->muted = 0; state->i2s_mode = 0; init_waitqueue_head(&state->wq); + /* These are the reset input/output positions */ + state->routing.input = MSP_INPUT_DEFAULT; + state->routing.output = MSP_OUTPUT_DEFAULT; state->rev1 = msp_read_dsp(client, 0x1e); if (state->rev1 != -1) diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h index 04821ebfe04a..1940748bb633 100644 --- a/drivers/media/video/msp3400-driver.h +++ b/drivers/media/video/msp3400-driver.h @@ -4,6 +4,8 @@ #ifndef MSP3400_DRIVER_H #define MSP3400_DRIVER_H +#include + /* ---------------------------------------------------------------------- */ /* This macro is allowed for *constants* only, gcc must calculate it @@ -72,7 +74,7 @@ struct msp_state { int i2s_mode; int main, second; /* sound carrier */ int input; - int source; /* see msp34xxg_set_source */ + struct v4l2_routing routing; /* v4l2 */ int audmode; diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index 9ee8dc216d7f..1c794c3b9f21 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -187,13 +187,14 @@ void msp3400c_set_mode(struct i2c_client *client, int mode) { struct msp_state *state = i2c_get_clientdata(client); struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; + int tuner = (state->routing.input >> 3) & 1; int i; v4l_dbg(1, msp_debug, client, "set_mode: %d\n", mode); state->mode = mode; state->rxsubchans = V4L2_TUNER_SUB_MONO; - msp_write_dem(client, 0x00bb, data->ad_cv); + msp_write_dem(client, 0x00bb, data->ad_cv | (tuner ? 0x100 : 0)); for (i = 5; i >= 0; i--) /* fir 1 */ msp_write_dem(client, 0x0001, data->fir1[i]); @@ -783,34 +784,6 @@ int msp3410d_thread(void *data) * struct msp: only norm, acb and source are really used in this mode */ -/* set the same 'source' for the loudspeaker, scart and quasi-peak detector - * the value for source is the same as bit 15:8 of DSP registers 0x08, - * 0x0a and 0x0c: 0=mono, 1=stereo or A|B, 2=SCART, 3=stereo or A, 4=stereo or B - */ -static void msp34xxg_set_source(struct i2c_client *client, int source) -{ - struct msp_state *state = i2c_get_clientdata(client); - - /* fix matrix mode to stereo and let the msp choose what - * to output according to 'source', as recommended - * for MONO (source==0) downmixing set bit[7:0] to 0x30 - */ - int value = (source & 0x07) << 8 | (source == 0 ? 0x30 : 0x20); - - v4l_dbg(1, msp_debug, client, "set source to %d (0x%x)\n", source, value); - msp_set_source(client, value); - /* - * set identification threshold. Personally, I - * I set it to a higher value that the default - * of 0x190 to ignore noisy stereo signals. - * this needs tuning. (recommended range 0x00a0-0x03c0) - * 0x7f0 = forced mono mode - */ - /* a2 threshold for stereo/bilingual */ - msp_write_dem(client, 0x22, msp_stereo_thresh); - state->source = source; -} - static int msp34xxg_modus(struct i2c_client *client) { struct msp_state *state = i2c_get_clientdata(client); @@ -843,10 +816,65 @@ static int msp34xxg_modus(struct i2c_client *client) return 0x0001; } +static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) + { + struct msp_state *state = i2c_get_clientdata(client); + int source, matrix; + + switch (state->audmode) { + case V4L2_TUNER_MODE_MONO: + source = 0; /* mono only */ + matrix = 0x30; + break; + case V4L2_TUNER_MODE_LANG1: + source = 3; /* stereo or A */ + matrix = 0x00; + break; + case V4L2_TUNER_MODE_LANG2: + source = 4; /* stereo or B */ + matrix = 0x10; + break; + case V4L2_TUNER_MODE_STEREO: + default: + source = 1; /* stereo or A|B */ + matrix = 0x20; + break; + } + + if (in == MSP_DSP_OUT_TUNER) + source = (source << 8) | 0x20; + /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14 + instead of 11, 12, 13. So we add one for that msp version. */ + else if (in >= MSP_DSP_OUT_MAIN_AVC && state->has_dolby_pro_logic) + source = ((in + 1) << 8) | matrix; + else + source = (in << 8) | matrix; + + v4l_dbg(1, msp_debug, client, "set source to %d (0x%x) for output %02x\n", + in, source, reg); + msp_write_dsp(client, reg, source); +} + +static void msp34xxg_set_sources(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + u32 in = state->routing.input; + + msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); + /* quasi-peak detector is set to same input as the loudspeaker (MAIN) */ + msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf); + msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf); + msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf); + if (state->has_scart23_in_scart2_out) + msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf); + msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf); +} + /* (re-)initialize the msp34xxg */ static void msp34xxg_reset(struct i2c_client *client) { struct msp_state *state = i2c_get_clientdata(client); + int tuner = (state->routing.input >> 3) & 1; int modus; /* initialize std to 1 (autodetect) to signal that no standard is @@ -864,11 +892,12 @@ static void msp34xxg_reset(struct i2c_client *client) /* step-by-step initialisation, as described in the manual */ modus = msp34xxg_modus(client); + modus |= tuner ? 0x100 : 0; msp_write_dem(client, 0x30, modus); /* write the dsps that may have an influence on standard/audio autodetection right now */ - msp34xxg_set_source(client, state->source); + msp34xxg_set_sources(client); msp_write_dsp(client, 0x0d, 0x1900); /* scart */ msp_write_dsp(client, 0x0e, 0x3000); /* FM */ @@ -896,7 +925,6 @@ int msp34xxg_thread(void *data) v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); - state->source = 1; /* default */ for (;;) { v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n"); msp_sleep(state, -1); @@ -993,7 +1021,6 @@ static int msp34xxg_detect_stereo(struct i2c_client *client) static void msp34xxg_set_audmode(struct i2c_client *client) { struct msp_state *state = i2c_get_clientdata(client); - int source; if (state->std == 0x20) { if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && @@ -1005,25 +1032,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client) } } - switch (state->audmode) { - case V4L2_TUNER_MODE_MONO: - source = 0; /* mono only */ - break; - case V4L2_TUNER_MODE_STEREO: - source = 1; /* stereo or A|B, see comment in msp34xxg_get_v4l2_stereo() */ - /* problem: that could also mean 2 (scart input) */ - break; - case V4L2_TUNER_MODE_LANG1: - source = 3; /* stereo or A */ - break; - case V4L2_TUNER_MODE_LANG2: - source = 4; /* stereo or B */ - break; - default: - source = 1; - break; - } - msp34xxg_set_source(client, source); + msp34xxg_set_sources(client); } void msp_set_audmode(struct i2c_client *client) diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 15fd55ff69ca..4e6d030d83c0 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1682,6 +1682,30 @@ static int chip_command(struct i2c_client *client, case VIDIOC_S_CTRL: return tvaudio_set_ctrl(chip, arg); + case VIDIOC_INT_G_AUDIO_ROUTING: + { + struct v4l2_routing *rt = arg; + + rt->input = chip->input; + rt->output = 0; + break; + } + + case VIDIOC_INT_S_AUDIO_ROUTING: + { + struct v4l2_routing *rt = arg; + + if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4) + return -EINVAL; + /* There are four inputs: tuner, radio, extern and intern. */ + chip->input = rt->input; + if (chip->muted) + break; + chip_write_masked(chip, desc->inputreg, + desc->inputmap[chip->input], desc->inputmask); + break; + } + case VIDIOC_S_AUDIO: { struct v4l2_audio *sarg = arg; diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index d1234d781e16..6824ee045fe6 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -312,7 +312,6 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(DECODER_DUMP)] = "DECODER_DUMP", #endif [_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO", - [_IOC_NR(MSP_SET_MATRIX)] = "MSP_SET_MATRIX", [_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR", [_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY", @@ -431,12 +430,6 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) printk ("%s: value=%d\n", s, *p); break; } - case MSP_SET_MATRIX: - { - struct msp_matrix *p=arg; - printk ("%s: input=%d, output=%d\n", s, p->input, p->output); - break; - } case VIDIOC_G_AUDIO: case VIDIOC_S_AUDIO: case VIDIOC_ENUMAUDIO: @@ -465,7 +458,7 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) struct v4l2_buffer *p=arg; struct v4l2_timecode *tc=&p->timecode; printk ("%s: %02ld:%02d:%02d.%08ld index=%d, type=%s, " - "bytesused=%d, flags=0x%08d, " + "bytesused=%d, flags=0x%08x, " "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx\n", s, (p->timestamp.tv_sec/3600), @@ -479,7 +472,7 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) prt_names(p->memory,v4l2_memory_names), p->m.userptr); printk ("%s: timecode= %02d:%02d:%02d type=%d, " - "flags=0x%08d, frames=%d, userbits=0x%p", + "flags=0x%08x, frames=%d, userbits=0x%08p\n", s,tc->hours,tc->minutes,tc->seconds, tc->type, tc->flags, tc->frames, tc->userbits); break; @@ -665,7 +658,7 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) case VIDIOC_INT_G_VIDEO_ROUTING: { struct v4l2_routing *p=arg; - printk ("%s: input=%d, output=%d\n", s, p->input, p->output); + printk ("%s: input=0x%x, output=0x%x\n", s, p->input, p->output); break; } case VIDIOC_G_SLICED_VBI_CAP: diff --git a/include/media/msp3400.h b/include/media/msp3400.h index 26e26585273f..0be61a021d45 100644 --- a/include/media/msp3400.h +++ b/include/media/msp3400.h @@ -145,9 +145,14 @@ MSP_DSP_TO_SCART1(sc_i2s_src) | \ MSP_DSP_TO_SCART2(sc_i2s_src) | \ MSP_DSP_TO_I2S(sc_i2s_src)) +#define MSP_INPUT_DEFAULT MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, \ + MSP_DSP_OUT_TUNER, MSP_DSP_OUT_TUNER) #define MSP_OUTPUT(sc) \ (MSP_OUT_TO_SCART1(sc) | \ MSP_OUT_TO_SCART2(sc)) +/* This equals the RESET position of the msp3400 ACB register */ +#define MSP_OUTPUT_DEFAULT (MSP_OUT_TO_SCART1(MSP_OUT_SCART3) | \ + MSP_OUT_TO_SCART2(MSP_OUT_SCART1_DA)) /* Tuner inputs vs. msp version */ /* Chip TUNER_1 TUNER_2 diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 07130474a0df..642520acdfa7 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -123,14 +123,6 @@ enum v4l2_chip_ident { /* v4l device was opened in Radio mode, to be replaced by VIDIOC_INT_S_TUNER_MODE */ #define AUDC_SET_RADIO _IO('d',88) -/* msp3400 ioctl: will be removed in the near future, to be replaced by - VIDIOC_INT_S_AUDIO_ROUTING. */ -struct msp_matrix { - int input; - int output; -}; -#define MSP_SET_MATRIX _IOW('m',17,struct msp_matrix) - /* tuner ioctls */ /* Sets tuner type and its I2C addr */ -- cgit v1.2.3 From 301e22d69140898eddd38a9134da711cb5dfc170 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 18 Mar 2006 17:15:00 -0300 Subject: V4L/DVB (3584): Implement V4L2_TUNER_MODE_LANG1_LANG2 audio mode Add a new audio mode V4L2_TUNER_MODE_LANG1_LANG2 (used by VIDIOC_G/S_TUNER). This mode allows the user to select both languages of a bilingual transmission, one language on the left, one on the right audio channel. If there is no bilingual transmission, or it is not supported, then this mode should act like V4L2_TUNER_MODE_STEREO. This mode is introduced for PVR-like drivers where it is useful to be able to record both languages of a bilingual broadcast. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ttpci/av7110_v4l.c | 5 +++++ drivers/media/video/bt8xx/bttv-driver.c | 3 ++- drivers/media/video/cx25840/cx25840-core.c | 5 +++-- drivers/media/video/cx88/cx88-tvaudio.c | 3 +++ drivers/media/video/msp3400-kthreads.c | 8 ++++++-- drivers/media/video/mxb.c | 6 ++++++ drivers/media/video/saa7134/saa7134-tvaudio.c | 2 ++ drivers/media/video/tvaudio.c | 1 + include/linux/videodev2.h | 1 + 9 files changed, 29 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index 2f23ceab8d44..603a22e4bfe2 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -369,6 +369,11 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) fm_matrix = 0x3001; // stereo src = 0x0020; break; + case V4L2_TUNER_MODE_LANG1_LANG2: + dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n"); + fm_matrix = 0x3000; // bilingual + src = 0x0020; + break; case V4L2_TUNER_MODE_LANG1: dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"); fm_matrix = 0x3000; // mono diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 80e4a7406ac2..74def9c23952 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -1878,7 +1878,8 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); if (t->audmode == V4L2_TUNER_MODE_MONO) va.mode = VIDEO_SOUND_MONO; - else if (t->audmode == V4L2_TUNER_MODE_STEREO) + else if (t->audmode == V4L2_TUNER_MODE_STEREO || + t->audmode == V4L2_TUNER_MODE_LANG1_LANG2) va.mode = VIDEO_SOUND_STEREO; else if (t->audmode == V4L2_TUNER_MODE_LANG1) va.mode = VIDEO_SOUND_LANG1; diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 7aee37645d63..a65b3cc4bf03 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -810,13 +810,14 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, bilingual -> lang1 */ cx25840_and_or(client, 0x809, ~0xf, 0x00); break; + case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1: /* mono -> mono stereo -> stereo bilingual -> lang1 */ cx25840_and_or(client, 0x809, ~0xf, 0x04); break; - case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: /* mono -> mono stereo -> stereo bilingual -> lang1/lang2 */ @@ -824,7 +825,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, break; case V4L2_TUNER_MODE_LANG2: /* mono -> mono - stereo ->stereo + stereo -> stereo bilingual -> lang2 */ cx25840_and_or(client, 0x809, ~0xf, 0x01); break; diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index da8d97ce0c4b..641a0c5a6490 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -885,6 +885,7 @@ void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual) set_audio_standard_BTSC(core, 1, EN_BTSC_FORCE_SAP); break; case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_STEREO); break; } @@ -905,6 +906,7 @@ void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual) EN_NICAM_FORCE_MONO2); break; case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: set_audio_standard_NICAM(core, EN_NICAM_FORCE_STEREO); break; @@ -926,6 +928,7 @@ void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual) EN_A2_FORCE_MONO2); break; case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: set_audio_standard_A2(core, EN_A2_FORCE_STEREO); break; diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index 1c794c3b9f21..c3984ea9ca07 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -223,9 +223,9 @@ void msp3400c_set_mode(struct i2c_client *client, int mode) nor do they support stereo BTSC. */ static void msp3400c_set_audmode(struct i2c_client *client) { - static char *strmode[] = { "mono", "stereo", "lang2", "lang1" }; + static char *strmode[] = { "mono", "stereo", "lang2", "lang1", "lang1+lang2" }; struct msp_state *state = i2c_get_clientdata(client); - char *modestr = (state->audmode >= 0 && state->audmode < 4) ? + char *modestr = (state->audmode >= 0 && state->audmode < 5) ? strmode[state->audmode] : "unknown"; int src = 0; /* channel source: FM/AM, nicam or SCART */ @@ -250,6 +250,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) case V4L2_TUNER_MODE_MONO: case V4L2_TUNER_MODE_LANG1: case V4L2_TUNER_MODE_LANG2: + case V4L2_TUNER_MODE_LANG1_LANG2: msp_write_dsp(client, 0x000e, 0x3000); break; } @@ -261,6 +262,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); break; case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: msp3400c_set_carrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); break; case V4L2_TUNER_MODE_LANG1: @@ -296,6 +298,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) /* switch audio */ switch (state->audmode) { case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: src |= 0x0020; break; case V4L2_TUNER_MODE_MONO: @@ -835,6 +838,7 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) matrix = 0x10; break; case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: default: source = 1; /* stereo or A|B */ matrix = 0x20; diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 14ca251787d5..b0aea4002d11 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -790,6 +790,12 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n")); break; } + case V4L2_TUNER_MODE_LANG1_LANG2: { + mxb->cur_mode = V4L2_TUNER_MODE_LANG1_LANG2; + byte = TDA9840_SET_BOTH; + DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n")); + break; + } case V4L2_TUNER_MODE_LANG1: { mxb->cur_mode = V4L2_TUNER_MODE_LANG1; byte = TDA9840_SET_LANG1; diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c index 3043233a8b6e..0db53d192b2a 100644 --- a/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -482,12 +482,14 @@ static int tvaudio_setstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *au [ V4L2_TUNER_MODE_STEREO ] = "stereo", [ V4L2_TUNER_MODE_LANG1 ] = "lang1", [ V4L2_TUNER_MODE_LANG2 ] = "lang2", + [ V4L2_TUNER_MODE_LANG1_LANG2 ] = "lang1+lang2", }; static u32 fm[] = { [ V4L2_TUNER_MODE_MONO ] = 0x00, /* ch1 */ [ V4L2_TUNER_MODE_STEREO ] = 0x80, /* auto */ [ V4L2_TUNER_MODE_LANG1 ] = 0x00, /* ch1 */ [ V4L2_TUNER_MODE_LANG2 ] = 0x01, /* ch2 */ + [ V4L2_TUNER_MODE_LANG1_LANG2 ] = 0x80, /* auto */ }; u32 reg; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 4e6d030d83c0..356bff455ad1 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1733,6 +1733,7 @@ static int chip_command(struct i2c_client *client, mode = VIDEO_SOUND_MONO; break; case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: mode = VIDEO_SOUND_STEREO; break; case V4L2_TUNER_MODE_LANG1: diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 724cfbf54b8a..2275bfec5b68 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -883,6 +883,7 @@ struct v4l2_modulator #define V4L2_TUNER_MODE_LANG2 0x0002 #define V4L2_TUNER_MODE_SAP 0x0002 #define V4L2_TUNER_MODE_LANG1 0x0003 +#define V4L2_TUNER_MODE_LANG1_LANG2 0x0004 struct v4l2_frequency { -- cgit v1.2.3 From a20c522498330ba0f4970a9bcd11890312277ae2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 23 Mar 2006 19:37:58 -0300 Subject: V4L/DVB (3598): Add bit algorithm adapter for the Conexant CX2341X boards. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/linux/i2c-id.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index ec311bc89439..679b46a6a565 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -184,6 +184,7 @@ #define I2C_HW_B_SAVAGE 0x01001d /* savage framebuffer driver */ #define I2C_HW_B_RADEON 0x01001e /* radeon framebuffer driver */ #define I2C_HW_B_EM28XX 0x01001f /* em28xx video capture cards */ +#define I2C_HW_B_CX2341X 0x010020 /* Conexant CX2341X MPEG encoder cards */ /* --- PCF 8584 based algorithms */ #define I2C_HW_P_LP 0x020000 /* Parallel port interface */ -- cgit v1.2.3 From 7fa033b103bc3f5c37f934695473f63adf140dba Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 23 Mar 2006 20:12:26 -0300 Subject: V4L/DVB (3599): Implement new routing commands for wm8775 and cs53l32a. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cs53l32a.c | 15 ++++++++++----- drivers/media/video/wm8775.c | 15 ++++++++++----- include/media/cs53l32a.h | 34 ++++++++++++++++++++++++++++++++++ include/media/wm8775.h | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 include/media/cs53l32a.h create mode 100644 include/media/wm8775.h (limited to 'include') diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index bc3331870240..de87247c74ee 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -59,20 +59,25 @@ static int cs53l32a_read(struct i2c_client *client, u8 reg) static int cs53l32a_command(struct i2c_client *client, unsigned int cmd, void *arg) { - struct v4l2_audio *input = arg; + struct v4l2_routing *route = arg; struct v4l2_control *ctrl = arg; switch (cmd) { - case VIDIOC_S_AUDIO: + case VIDIOC_INT_G_AUDIO_ROUTING: + route->input = (cs53l32a_read(client, 0x01) >> 4) & 3; + route->output = 0; + break; + + case VIDIOC_INT_S_AUDIO_ROUTING: /* There are 2 physical inputs, but the second input can be placed in two modes, the first mode bypasses the PGA (gain), the second goes through the PGA. Hence there are three possible inputs to choose from. */ - if (input->index > 2) { - v4l_err(client, "Invalid input %d.\n", input->index); + if (route->input > 2) { + v4l_err(client, "Invalid input %d.\n", route->input); return -EINVAL; } - cs53l32a_write(client, 0x01, 0x01 + (input->index << 4)); + cs53l32a_write(client, 0x01, 0x01 + (route->input << 4)); break; case VIDIOC_G_CTRL: diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 9b90225226e6..d81a88bbe43d 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -79,21 +79,26 @@ static int wm8775_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct wm8775_state *state = i2c_get_clientdata(client); - struct v4l2_audio *input = arg; + struct v4l2_routing *route = arg; struct v4l2_control *ctrl = arg; switch (cmd) { - case VIDIOC_S_AUDIO: + case VIDIOC_INT_G_AUDIO_ROUTING: + route->input = state->input; + route->output = 0; + break; + + case VIDIOC_INT_S_AUDIO_ROUTING: /* There are 4 inputs and one output. Zero or more inputs are multiplexed together to the output. Hence there are 16 combinations. If only one input is active (the normal case) then the input values 1, 2, 4 or 8 should be used. */ - if (input->index > 15) { - v4l_err(client, "Invalid input %d.\n", input->index); + if (route->input > 15) { + v4l_err(client, "Invalid input %d.\n", route->input); return -EINVAL; } - state->input = input->index; + state->input = route->input; if (state->muted) break; wm8775_write(client, R21, 0x0c0); diff --git a/include/media/cs53l32a.h b/include/media/cs53l32a.h new file mode 100644 index 000000000000..bf76197d3790 --- /dev/null +++ b/include/media/cs53l32a.h @@ -0,0 +1,34 @@ +/* + cs53l32a.h - definition for cs53l32a inputs and outputs + + Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl) + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _CS53L32A_H_ +#define _CS53L32A_H_ + +/* There are 2 physical inputs, but the second input can be + placed in two modes, the first mode bypasses the PGA (gain), + the second goes through the PGA. Hence there are three + possible inputs to choose from. */ + +/* CS53L32A HW inputs */ +#define CS53L32A_IN0 0 +#define CS53L32A_IN1 1 +#define CS53L32A_IN2 2 + +#endif diff --git a/include/media/wm8775.h b/include/media/wm8775.h new file mode 100644 index 000000000000..60739c5a23ae --- /dev/null +++ b/include/media/wm8775.h @@ -0,0 +1,35 @@ +/* + wm8775.h - definition for wm8775 inputs and outputs + + Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl) + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _WM8775_H_ +#define _WM8775_H_ + +/* The WM8775 has 4 inputs and one output. Zero or more inputs + are multiplexed together to the output. Hence there are + 16 combinations. + If only one input is active (the normal case) then the + input values 1, 2, 4 or 8 should be used. */ + +#define WM8775_AIN1 1 +#define WM8775_AIN2 2 +#define WM8775_AIN3 4 +#define WM8775_AIN4 8 + +#endif -- cgit v1.2.3 From b17ea91a43ea0c746ab4cabb698275e1771ed23d Mon Sep 17 00:00:00 2001 From: "Chen, Kenneth W" Date: Sun, 12 Mar 2006 11:10:00 -0800 Subject: [IA64] cleanup dig_irq_init dig_irq_init is equivalent to machvec_noop, no need to define another empty function. Signed-off-by: Ken Chen Signed-off-by: Tony Luck --- arch/ia64/dig/setup.c | 5 ----- include/asm-ia64/machvec_dig.h | 2 -- 2 files changed, 7 deletions(-) (limited to 'include') diff --git a/arch/ia64/dig/setup.c b/arch/ia64/dig/setup.c index c9104bfff667..38aa9c108857 100644 --- a/arch/ia64/dig/setup.c +++ b/arch/ia64/dig/setup.c @@ -69,8 +69,3 @@ dig_setup (char **cmdline_p) screen_info.orig_video_isVGA = 1; /* XXX fake */ screen_info.orig_video_ega_bx = 3; /* XXX fake */ } - -void __init -dig_irq_init (void) -{ -} diff --git a/include/asm-ia64/machvec_dig.h b/include/asm-ia64/machvec_dig.h index 4dc8522c974f..8a0752f40987 100644 --- a/include/asm-ia64/machvec_dig.h +++ b/include/asm-ia64/machvec_dig.h @@ -2,7 +2,6 @@ #define _ASM_IA64_MACHVEC_DIG_h extern ia64_mv_setup_t dig_setup; -extern ia64_mv_irq_init_t dig_irq_init; /* * This stuff has dual use! @@ -13,6 +12,5 @@ extern ia64_mv_irq_init_t dig_irq_init; */ #define platform_name "dig" #define platform_setup dig_setup -#define platform_irq_init dig_irq_init #endif /* _ASM_IA64_MACHVEC_DIG_h */ -- cgit v1.2.3 From f90aa8c4febb306e1266e1ad34fd8464e201aa7f Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Wed, 8 Mar 2006 13:30:18 -0500 Subject: [IA64] Tollhouse HP: IA64 arch changes arch/ia64/sn and include/asm-ia64/sn changes required to support Tollhouse system PCI hotplug, fixes the ia64_sn_sysctl_ioboard_get call, and introduces the PRF_HOTPLUG_SUPPORT feature bit. Signed-off-by: Prarit Bhargava Signed-off-by: Tony Luck --- arch/ia64/sn/kernel/io_init.c | 29 +++++++++++++++++++++++++++++ arch/ia64/sn/kernel/tiocx.c | 10 ++++++++-- arch/ia64/sn/pci/pcibr/pcibr_provider.c | 17 +++++++++++++++++ include/asm-ia64/sn/l1.h | 3 ++- include/asm-ia64/sn/pcibr_provider.h | 1 + include/asm-ia64/sn/pcidev.h | 1 + include/asm-ia64/sn/sn_feature_sets.h | 3 +-- include/asm-ia64/sn/sn_sal.h | 26 +++++++++++++++----------- 8 files changed, 74 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c index dfb3f2902379..5101ac462643 100644 --- a/arch/ia64/sn/kernel/io_init.c +++ b/arch/ia64/sn/kernel/io_init.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -710,9 +712,36 @@ cnodeid_get_geoid(cnodeid_t cnode) return hubdev->hdi_geoid; } +void sn_generate_path(struct pci_bus *pci_bus, char *address) +{ + nasid_t nasid; + cnodeid_t cnode; + geoid_t geoid; + moduleid_t moduleid; + u16 bricktype; + + nasid = NASID_GET(SN_PCIBUS_BUSSOFT(pci_bus)->bs_base); + cnode = nasid_to_cnodeid(nasid); + geoid = cnodeid_get_geoid(cnode); + moduleid = geo_module(geoid); + + sprintf(address, "module_%c%c%c%c%.2d", + '0'+RACK_GET_CLASS(MODULE_GET_RACK(moduleid)), + '0'+RACK_GET_GROUP(MODULE_GET_RACK(moduleid)), + '0'+RACK_GET_NUM(MODULE_GET_RACK(moduleid)), + MODULE_GET_BTCHAR(moduleid), MODULE_GET_BPOS(moduleid)); + + /* Tollhouse requires slot id to be displayed */ + bricktype = MODULE_GET_BTYPE(moduleid); + if ((bricktype == L1_BRICKTYPE_191010) || + (bricktype == L1_BRICKTYPE_1932)) + sprintf(address, "%s^%d", address, geo_slot(geoid)); +} + subsys_initcall(sn_pci_init); EXPORT_SYMBOL(sn_pci_fixup_slot); EXPORT_SYMBOL(sn_pci_unfixup_slot); EXPORT_SYMBOL(sn_pci_controller_fixup); EXPORT_SYMBOL(sn_bus_store_sysdata); EXPORT_SYMBOL(sn_bus_free_sysdata); +EXPORT_SYMBOL(sn_generate_path); diff --git a/arch/ia64/sn/kernel/tiocx.c b/arch/ia64/sn/kernel/tiocx.c index 99cb28e74295..feaf1a6e8101 100644 --- a/arch/ia64/sn/kernel/tiocx.c +++ b/arch/ia64/sn/kernel/tiocx.c @@ -369,9 +369,15 @@ static void tio_corelet_reset(nasid_t nasid, int corelet) static int is_fpga_tio(int nasid, int *bt) { - int ioboard_type; + u16 ioboard_type; + s64 rc; - ioboard_type = ia64_sn_sysctl_ioboard_get(nasid); + rc = ia64_sn_sysctl_ioboard_get(nasid, &ioboard_type); + if (rc) { + printk(KERN_WARNING "ia64_sn_sysctl_ioboard_get failed: %ld\n", + rc); + return 0; + } switch (ioboard_type) { case L1_BRICKTYPE_SA: diff --git a/arch/ia64/sn/pci/pcibr/pcibr_provider.c b/arch/ia64/sn/pci/pcibr/pcibr_provider.c index 98f716bd92f0..ab1211ef0176 100644 --- a/arch/ia64/sn/pci/pcibr/pcibr_provider.c +++ b/arch/ia64/sn/pci/pcibr/pcibr_provider.c @@ -74,6 +74,22 @@ static int sal_pcibr_error_interrupt(struct pcibus_info *soft) return (int)ret_stuff.v0; } +u16 sn_ioboard_to_pci_bus(struct pci_bus *pci_bus) +{ + s64 rc; + u16 ioboard; + nasid_t nasid = NASID_GET(SN_PCIBUS_BUSSOFT(pci_bus)->bs_base); + + rc = ia64_sn_sysctl_ioboard_get(nasid, &ioboard); + if (rc) { + printk(KERN_WARNING "ia64_sn_sysctl_ioboard_get failed: %ld\n", + rc); + return 0; + } + + return ioboard; +} + /* * PCI Bridge Error interrupt handler. Gets invoked whenever a PCI * bridge sends an error interrupt. @@ -255,3 +271,4 @@ pcibr_init_provider(void) EXPORT_SYMBOL_GPL(sal_pcibr_slot_enable); EXPORT_SYMBOL_GPL(sal_pcibr_slot_disable); +EXPORT_SYMBOL_GPL(sn_ioboard_to_pci_bus); diff --git a/include/asm-ia64/sn/l1.h b/include/asm-ia64/sn/l1.h index e3b819110d47..344bf44bb356 100644 --- a/include/asm-ia64/sn/l1.h +++ b/include/asm-ia64/sn/l1.h @@ -34,6 +34,8 @@ #define L1_BRICKTYPE_IA 0x6b /* k */ #define L1_BRICKTYPE_ATHENA 0x2b /* + */ #define L1_BRICKTYPE_DAYTONA 0x7a /* z */ +#define L1_BRICKTYPE_1932 0x2c /* . */ +#define L1_BRICKTYPE_191010 0x2e /* , */ /* board type response codes */ #define L1_BOARDTYPE_IP69 0x0100 /* CA */ @@ -46,5 +48,4 @@ #define L1_BOARDTYPE_DAYTONA 0x0800 /* AD */ #define L1_BOARDTYPE_INVAL (-1) /* invalid brick type */ - #endif /* _ASM_IA64_SN_L1_H */ diff --git a/include/asm-ia64/sn/pcibr_provider.h b/include/asm-ia64/sn/pcibr_provider.h index a601d3af39b6..51260ab70d91 100644 --- a/include/asm-ia64/sn/pcibr_provider.h +++ b/include/asm-ia64/sn/pcibr_provider.h @@ -144,4 +144,5 @@ extern int sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp); extern int sal_pcibr_slot_disable(struct pcibus_info *soft, int device, int action, void *resp); +extern u16 sn_ioboard_to_pci_bus(struct pci_bus *pci_bus); #endif diff --git a/include/asm-ia64/sn/pcidev.h b/include/asm-ia64/sn/pcidev.h index 38cdffbc4c7b..eac3561574be 100644 --- a/include/asm-ia64/sn/pcidev.h +++ b/include/asm-ia64/sn/pcidev.h @@ -76,6 +76,7 @@ extern void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus); extern void sn_bus_store_sysdata(struct pci_dev *dev); extern void sn_bus_free_sysdata(void); +extern void sn_generate_path(struct pci_bus *pci_bus, char *address); extern void sn_pci_fixup_slot(struct pci_dev *dev); extern void sn_pci_unfixup_slot(struct pci_dev *dev); extern void sn_irq_lh_init(void); diff --git a/include/asm-ia64/sn/sn_feature_sets.h b/include/asm-ia64/sn/sn_feature_sets.h index ff33e3bd3f8e..30dcfa442e53 100644 --- a/include/asm-ia64/sn/sn_feature_sets.h +++ b/include/asm-ia64/sn/sn_feature_sets.h @@ -30,8 +30,7 @@ extern int sn_prom_feature_available(int id); #define PRF_PAL_CACHE_FLUSH_SAFE 0 #define PRF_DEVICE_FLUSH_LIST 1 - - +#define PRF_HOTPLUG_SUPPORT 2 /* --------------------- OS Features -------------------------------*/ diff --git a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h index e77f0c9b7d3d..246f43a796d7 100644 --- a/include/asm-ia64/sn/sn_sal.h +++ b/include/asm-ia64/sn/sn_sal.h @@ -907,18 +907,22 @@ ia64_sn_sysctl_tio_clock_reset(nasid_t nasid) /* * Get the associated ioboard type for a given nasid. */ -static inline int -ia64_sn_sysctl_ioboard_get(nasid_t nasid) +static inline s64 +ia64_sn_sysctl_ioboard_get(nasid_t nasid, u16 *ioboard) { - struct ia64_sal_retval rv; - SAL_CALL_REENTRANT(rv, SN_SAL_SYSCTL_OP, SAL_SYSCTL_OP_IOBOARD, - nasid, 0, 0, 0, 0, 0); - if (rv.v0 != 0) - return (int)rv.v0; - if (rv.v1 != 0) - return (int)rv.v1; - - return 0; + struct ia64_sal_retval isrv; + SAL_CALL_REENTRANT(isrv, SN_SAL_SYSCTL_OP, SAL_SYSCTL_OP_IOBOARD, + nasid, 0, 0, 0, 0, 0); + if (isrv.v0 != 0) { + *ioboard = isrv.v0; + return isrv.status; + } + if (isrv.v1 != 0) { + *ioboard = isrv.v1; + return isrv.status; + } + + return isrv.status; } /** -- cgit v1.2.3 From b354a8388891adc5dc5e5fb0130f000152f3fb94 Mon Sep 17 00:00:00 2001 From: Jack Steiner Date: Thu, 2 Mar 2006 16:02:21 -0600 Subject: [IA64] Increase max node count on SN platforms Add a configuration option to allow the maximum number of nodes to be configurable for GENERIC or SN kernels. Signed-off-by: Jack Steiner Signed-off-by: Tony Luck --- arch/ia64/Kconfig | 9 +++++++++ include/asm-ia64/numnodes.h | 13 +++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index ff7ae6b664e8..10b6b9e7716b 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -252,6 +252,15 @@ config NR_CPUS than 64 will cause the use of a CPU mask array, causing a small performance hit. +config IA64_NR_NODES + int "Maximum number of NODEs (256-1024)" if (IA64_SGI_SN2 || IA64_GENERIC) + range 256 1024 + depends on IA64_SGI_SN2 || IA64_GENERIC + default "256" + help + This option specifies the maximum number of nodes in your SSI system. + If in doubt, use the default. + config HOTPLUG_CPU bool "Support for hot-pluggable CPUs (EXPERIMENTAL)" depends on SMP && EXPERIMENTAL diff --git a/include/asm-ia64/numnodes.h b/include/asm-ia64/numnodes.h index 21cff4da5485..e9d356f549d9 100644 --- a/include/asm-ia64/numnodes.h +++ b/include/asm-ia64/numnodes.h @@ -3,13 +3,18 @@ #ifdef CONFIG_IA64_DIG /* Max 8 Nodes */ -#define NODES_SHIFT 3 +# define NODES_SHIFT 3 #elif defined(CONFIG_IA64_HP_ZX1) || defined(CONFIG_IA64_HP_ZX1_SWIOTLB) /* Max 32 Nodes */ -#define NODES_SHIFT 5 +# define NODES_SHIFT 5 #elif defined(CONFIG_IA64_SGI_SN2) || defined(CONFIG_IA64_GENERIC) -/* Max 256 Nodes */ -#define NODES_SHIFT 8 +# if CONFIG_IA64_NR_NODES == 256 +# define NODES_SHIFT 8 +# elif CONFIG_IA64_NR_NODES <= 512 +# define NODES_SHIFT 9 +# elif CONFIG_IA64_NR_NODES <= 1024 +# define NODES_SHIFT 10 +# endif #endif #endif /* _ASM_MAX_NUMNODES_H */ -- cgit v1.2.3 From 3ad5ef8b9d0d0cc2d4b2c63e766ef903d482dfc7 Mon Sep 17 00:00:00 2001 From: Jack Steiner Date: Thu, 2 Mar 2006 16:02:25 -0600 Subject: [IA64] Increase max node count on SN platforms Add support in IA64 acpi for platforms that support more than 256 nodes. Currently, ACPI is limited to 256 nodes because the proximity domain number is 8-bits. Long term, we expect to use ACPI3.0 to support >256 nodes. This patch is an interim solution that works with platforms that pass the high order bits of the proximity domain in "reserved" fields of the ACPI tables. This code is enabled ONLY on SN platforms. Signed-off-by: Jack Steiner Signed-off-by: Tony Luck --- arch/ia64/kernel/acpi.c | 32 ++++++++++++++++++++++++++++---- include/asm-ia64/acpi.h | 4 ++++ 2 files changed, 32 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 24fe7c81e108..a4e218ce2edb 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -420,6 +420,26 @@ int __devinitdata pxm_to_nid_map[MAX_PXM_DOMAINS]; int __initdata nid_to_pxm_map[MAX_NUMNODES]; static struct acpi_table_slit __initdata *slit_table; +static int get_processor_proximity_domain(struct acpi_table_processor_affinity *pa) +{ + int pxm; + + pxm = pa->proximity_domain; + if (ia64_platform_is("sn2")) + pxm += pa->reserved[0] << 8; + return pxm; +} + +static int get_memory_proximity_domain(struct acpi_table_memory_affinity *ma) +{ + int pxm; + + pxm = ma->proximity_domain; + if (ia64_platform_is("sn2")) + pxm += ma->reserved1[0] << 8; + return pxm; +} + /* * ACPI 2.0 SLIT (System Locality Information Table) * http://devresource.hp.com/devresource/Docs/TechPapers/IA64/slit.pdf @@ -443,16 +463,20 @@ void __init acpi_numa_slit_init(struct acpi_table_slit *slit) void __init acpi_numa_processor_affinity_init(struct acpi_table_processor_affinity *pa) { + int pxm; + if (!pa->flags.enabled) return; + pxm = get_processor_proximity_domain(pa); + /* record this node in proximity bitmap */ - pxm_bit_set(pa->proximity_domain); + pxm_bit_set(pxm); node_cpuid[srat_num_cpus].phys_id = (pa->apic_id << 8) | (pa->lsapic_eid); /* nid should be overridden as logical node id later */ - node_cpuid[srat_num_cpus].nid = pa->proximity_domain; + node_cpuid[srat_num_cpus].nid = pxm; srat_num_cpus++; } @@ -460,10 +484,10 @@ void __init acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma) { unsigned long paddr, size; - u8 pxm; + int pxm; struct node_memblk_s *p, *q, *pend; - pxm = ma->proximity_domain; + pxm = get_memory_proximity_domain(ma); /* fill node memory chunk structure */ paddr = ma->base_addr_hi; diff --git a/include/asm-ia64/acpi.h b/include/asm-ia64/acpi.h index f7a517654308..d734585a23cf 100644 --- a/include/asm-ia64/acpi.h +++ b/include/asm-ia64/acpi.h @@ -111,7 +111,11 @@ extern int additional_cpus; #ifdef CONFIG_ACPI_NUMA /* Proximity bitmap length; _PXM is at most 255 (8 bit)*/ +#ifdef CONFIG_IA64_NR_NODES +#define MAX_PXM_DOMAINS CONFIG_IA64_NR_NODES +#else #define MAX_PXM_DOMAINS (256) +#endif extern int __devinitdata pxm_to_nid_map[MAX_PXM_DOMAINS]; extern int __initdata nid_to_pxm_map[MAX_NUMNODES]; #endif -- cgit v1.2.3 From a9de98351436b25b3c2f234addb6d66a6a6f42f8 Mon Sep 17 00:00:00 2001 From: Jack Steiner Date: Thu, 2 Mar 2006 16:02:28 -0600 Subject: [IA64] Increase max node count on SN platforms Node number are kept in the cpu_to_node_map which is currently defined as u8. Change to u16 to accomodate larger node numbers. Signed-off-by: Jack Steiner Signed-off-by: Tony Luck --- arch/ia64/kernel/numa.c | 2 +- include/asm-ia64/numa.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/numa.c b/arch/ia64/kernel/numa.c index a68ce6678092..0766493d4d00 100644 --- a/arch/ia64/kernel/numa.c +++ b/arch/ia64/kernel/numa.c @@ -25,7 +25,7 @@ #include #include -u8 cpu_to_node_map[NR_CPUS] __cacheline_aligned; +u16 cpu_to_node_map[NR_CPUS] __cacheline_aligned; EXPORT_SYMBOL(cpu_to_node_map); cpumask_t node_to_cpu_mask[MAX_NUMNODES] __cacheline_aligned; diff --git a/include/asm-ia64/numa.h b/include/asm-ia64/numa.h index 3ae128fe0823..dae6aeb7b119 100644 --- a/include/asm-ia64/numa.h +++ b/include/asm-ia64/numa.h @@ -23,7 +23,7 @@ #include -extern u8 cpu_to_node_map[NR_CPUS] __cacheline_aligned; +extern u16 cpu_to_node_map[NR_CPUS] __cacheline_aligned; extern cpumask_t node_to_cpu_mask[MAX_NUMNODES] __cacheline_aligned; /* Stuff below this line could be architecture independent */ -- cgit v1.2.3 From 4129a953ad4db379d8e07b0dd2157998653a1325 Mon Sep 17 00:00:00 2001 From: Fenghua Yu Date: Mon, 27 Feb 2006 16:16:22 -0800 Subject: [IA64] New IA64 core/thread detection patch IPF SDM 2.2 changes definition of PAL_LOGICAL_TO_PHYSICAL to add proc_number=-1 to get core/thread mapping info on the running processer. Based on this change, we had better to update existing core/thread detection in IA64 kernel correspondingly. The attached patch implements this change. It simplifies detection code and eliminates potential race condition. It also runs a bit faster and has better scalability especially when cores and threads number grows up in one package. Signed-off-by: Fenghua Yu Signed-off-by: Tony Luck --- arch/ia64/kernel/smpboot.c | 109 ++------------------------------------------- include/asm-ia64/pal.h | 3 +- 2 files changed, 5 insertions(+), 107 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index c4b633b36dab..44e9547878ac 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -624,31 +624,7 @@ void __devinit smp_prepare_boot_cpu(void) per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; } -/* - * mt_info[] is a temporary store for all info returned by - * PAL_LOGICAL_TO_PHYSICAL, to be copied into cpuinfo_ia64 when the - * specific cpu comes. - */ -static struct { - __u32 socket_id; - __u16 core_id; - __u16 thread_id; - __u16 proc_fixed_addr; - __u8 valid; -} mt_info[NR_CPUS] __devinitdata; - #ifdef CONFIG_HOTPLUG_CPU -static inline void -remove_from_mtinfo(int cpu) -{ - int i; - - for_each_cpu(i) - if (mt_info[i].valid && mt_info[i].socket_id == - cpu_data(cpu)->socket_id) - mt_info[i].valid = 0; -} - static inline void clear_cpu_sibling_map(int cpu) { @@ -678,12 +654,6 @@ remove_siblinginfo(int cpu) /* remove it from all sibling map's */ clear_cpu_sibling_map(cpu); - - /* if this cpu is the last in the core group, remove all its info - * from mt_info structure - */ - if (last) - remove_from_mtinfo(cpu); } extern void fixup_irqs(void); @@ -878,40 +848,6 @@ init_smp_config(void) ia64_sal_strerror(sal_ret)); } -static inline int __devinit -check_for_mtinfo_index(void) -{ - int i; - - for_each_cpu(i) - if (!mt_info[i].valid) - return i; - - return -1; -} - -/* - * Search the mt_info to find out if this socket's cid/tid information is - * cached or not. If the socket exists, fill in the core_id and thread_id - * in cpuinfo - */ -static int __devinit -check_for_new_socket(__u16 logical_address, struct cpuinfo_ia64 *c) -{ - int i; - __u32 sid = c->socket_id; - - for_each_cpu(i) { - if (mt_info[i].valid && mt_info[i].proc_fixed_addr == logical_address - && mt_info[i].socket_id == sid) { - c->core_id = mt_info[i].core_id; - c->thread_id = mt_info[i].thread_id; - return 1; /* not a new socket */ - } - } - return 0; -} - /* * identify_siblings(cpu) gets called from identify_cpu. This populates the * information related to logical execution units in per_cpu_data structure. @@ -921,14 +857,12 @@ identify_siblings(struct cpuinfo_ia64 *c) { s64 status; u16 pltid; - u64 proc_fixed_addr; - int count, i; pal_logical_to_physical_t info; if (smp_num_cpucores == 1 && smp_num_siblings == 1) return; - if ((status = ia64_pal_logical_to_phys(0, &info)) != PAL_STATUS_SUCCESS) { + if ((status = ia64_pal_logical_to_phys(-1, &info)) != PAL_STATUS_SUCCESS) { printk(KERN_ERR "ia64_pal_logical_to_phys failed with %ld\n", status); return; @@ -937,47 +871,12 @@ identify_siblings(struct cpuinfo_ia64 *c) printk(KERN_ERR "ia64_sal_pltid failed with %ld\n", status); return; } - if ((status = ia64_pal_fixed_addr(&proc_fixed_addr)) != PAL_STATUS_SUCCESS) { - printk(KERN_ERR "ia64_pal_fixed_addr failed with %ld\n", status); - return; - } c->socket_id = (pltid << 8) | info.overview_ppid; c->cores_per_socket = info.overview_cpp; c->threads_per_core = info.overview_tpc; - count = c->num_log = info.overview_num_log; + c->num_log = info.overview_num_log; - /* If the thread and core id information is already cached, then - * we will simply update cpu_info and return. Otherwise, we will - * do the PAL calls and cache core and thread id's of all the siblings. - */ - if (check_for_new_socket(proc_fixed_addr, c)) - return; - - for (i = 0; i < count; i++) { - int index; - - if (i && (status = ia64_pal_logical_to_phys(i, &info)) - != PAL_STATUS_SUCCESS) { - printk(KERN_ERR "ia64_pal_logical_to_phys failed" - " with %ld\n", status); - return; - } - if (info.log2_la == proc_fixed_addr) { - c->core_id = info.log1_cid; - c->thread_id = info.log1_tid; - } - - index = check_for_mtinfo_index(); - /* We will not do the mt_info caching optimization in this case. - */ - if (index < 0) - continue; - - mt_info[index].valid = 1; - mt_info[index].socket_id = c->socket_id; - mt_info[index].core_id = info.log1_cid; - mt_info[index].thread_id = info.log1_tid; - mt_info[index].proc_fixed_addr = info.log2_la; - } + c->core_id = info.log1_cid; + c->thread_id = info.log1_tid; } diff --git a/include/asm-ia64/pal.h b/include/asm-ia64/pal.h index 7708ec669a33..4e7e6f23b08c 100644 --- a/include/asm-ia64/pal.h +++ b/include/asm-ia64/pal.h @@ -1640,8 +1640,7 @@ ia64_pal_logical_to_phys(u64 proc_number, pal_logical_to_physical_t *mapping) if (iprv.status == PAL_STATUS_SUCCESS) { - if (proc_number == 0) - mapping->overview.overview_data = iprv.v0; + mapping->overview.overview_data = iprv.v0; mapping->ppli1.ppli1_data = iprv.v1; mapping->ppli2.ppli2_data = iprv.v2; } -- cgit v1.2.3 From a1a8feed1743ec8d2af1dafa7c5321679f0a3e4f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 23 Mar 2006 22:07:34 -0800 Subject: [MODULES]: Don't allow statically declared exports Add an extern declaration for exported symbols to make the compiler warn on symbols declared statically. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/module.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index 70bd843c71cb..d9569151c182 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -183,6 +183,7 @@ void *__symbol_get_gpl(const char *symbol); /* For every exported symbol, place a struct in the __ksymtab section */ #define __EXPORT_SYMBOL(sym, sec) \ + extern typeof(sym) sym; \ __CRC_SYMBOL(sym, sec) \ static const char __kstrtab_##sym[] \ __attribute__((section("__ksymtab_strings"))) \ -- cgit v1.2.3 From 9932cf954691f20d548cd6010d89d1d48597e104 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 24 Mar 2006 15:12:37 -0800 Subject: [NET]: Fill in a 32-bit hole in struct sock on 64-bit platforms. This makes struct sock 8 bytes smaller on 64-bit. Signed-off-by: David S. Miller --- include/net/sock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index ec226f31dc2a..2aa73c0ec6c2 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -210,6 +210,7 @@ struct sock { gfp_t sk_allocation; int sk_sndbuf; int sk_route_caps; + int sk_rcvlowat; unsigned long sk_flags; unsigned long sk_lingertime; /* @@ -230,7 +231,6 @@ struct sock { unsigned short sk_max_ack_backlog; __u32 sk_priority; struct ucred sk_peercred; - int sk_rcvlowat; long sk_rcvtimeo; long sk_sndtimeo; struct sk_filter *sk_filter; -- cgit v1.2.3 From cef2685e0053945ea0f3c02297386b040f486ea7 Mon Sep 17 00:00:00 2001 From: Ilia Sotnikov Date: Sat, 25 Mar 2006 01:38:55 -0800 Subject: [IPV4]: Aggregate route entries with different TOS values When we get an ICMP need-to-frag message, the original TOS value in the ICMP payload cannot be used as a key to look up the routes to update. This is because the TOS field may have been modified by routers on the way. Similarly, ip_rt_redirect should also ignore the TOS as the router that gave us the message may have modified the TOS value. The patch achieves this objective by aggregating entries with different TOS values (but are otherwise identical) into the same bucket. This makes it easy to update them at the same time when an ICMP message is received. In future we should use a twin-hashing scheme where teh aggregation occurs at the entry level. That is, the TOS goes back into the hash for normal lookups while ICMP lookups will end up with a node that gives us a list that contains all other route entries that differ only by TOS. Signed-off-by: Ilia Sotnikov Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/net/route.h | 2 +- net/ipv4/icmp.c | 2 +- net/ipv4/route.c | 45 ++++++++++++++++++--------------------------- 3 files changed, 20 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index 9c04f15090d2..98c915abdec8 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -110,7 +110,7 @@ extern struct ip_rt_acct *ip_rt_acct; struct in_device; extern int ip_rt_init(void); extern void ip_rt_redirect(u32 old_gw, u32 dst, u32 new_gw, - u32 src, u8 tos, struct net_device *dev); + u32 src, struct net_device *dev); extern void ip_rt_advice(struct rtable **rp, int advice); extern void rt_cache_flush(int how); extern int __ip_route_output_key(struct rtable **, const struct flowi *flp); diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index e7bbff4340bb..9831fd2c73a0 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -753,7 +753,7 @@ static void icmp_redirect(struct sk_buff *skb) case ICMP_REDIR_HOST: case ICMP_REDIR_HOSTTOS: ip_rt_redirect(skb->nh.iph->saddr, ip, skb->h.icmph->un.gateway, - iph->saddr, iph->tos, skb->dev); + iph->saddr, skb->dev); break; } out: diff --git a/net/ipv4/route.c b/net/ipv4/route.c index fca5fe0cf94a..94fcbc5e5a1b 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -55,6 +55,8 @@ * Robert Olsson : Added rt_cache statistics * Arnaldo C. Melo : Convert proc stuff to seq_file * Eric Dumazet : hashed spinlocks and rt_check_expire() fixes. + * Ilia Sotnikov : Ignore TOS on PMTUD and Redirect + * Ilia Sotnikov : Removed TOS from hash calculations * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -247,9 +249,9 @@ static DEFINE_PER_CPU(struct rt_cache_stat, rt_cache_stat); static int rt_intern_hash(unsigned hash, struct rtable *rth, struct rtable **res); -static unsigned int rt_hash_code(u32 daddr, u32 saddr, u8 tos) +static unsigned int rt_hash_code(u32 daddr, u32 saddr) { - return (jhash_3words(daddr, saddr, (u32) tos, rt_hash_rnd) + return (jhash_2words(daddr, saddr, rt_hash_rnd) & rt_hash_mask); } @@ -1111,7 +1113,7 @@ static void rt_del(unsigned hash, struct rtable *rt) } void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, - u32 saddr, u8 tos, struct net_device *dev) + u32 saddr, struct net_device *dev) { int i, k; struct in_device *in_dev = in_dev_get(dev); @@ -1119,8 +1121,6 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, u32 skeys[2] = { saddr, 0 }; int ikeys[2] = { dev->ifindex, 0 }; - tos &= IPTOS_RT_MASK; - if (!in_dev) return; @@ -1141,8 +1141,7 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, for (i = 0; i < 2; i++) { for (k = 0; k < 2; k++) { unsigned hash = rt_hash_code(daddr, - skeys[i] ^ (ikeys[k] << 5), - tos); + skeys[i] ^ (ikeys[k] << 5)); rthp=&rt_hash_table[hash].chain; @@ -1152,7 +1151,6 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, if (rth->fl.fl4_dst != daddr || rth->fl.fl4_src != skeys[i] || - rth->fl.fl4_tos != tos || rth->fl.oif != ikeys[k] || rth->fl.iif != 0) { rthp = &rth->u.rt_next; @@ -1232,10 +1230,9 @@ reject_redirect: if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit()) printk(KERN_INFO "Redirect from %u.%u.%u.%u on %s about " "%u.%u.%u.%u ignored.\n" - " Advised path = %u.%u.%u.%u -> %u.%u.%u.%u, " - "tos %02x\n", + " Advised path = %u.%u.%u.%u -> %u.%u.%u.%u\n", NIPQUAD(old_gw), dev->name, NIPQUAD(new_gw), - NIPQUAD(saddr), NIPQUAD(daddr), tos); + NIPQUAD(saddr), NIPQUAD(daddr)); #endif in_dev_put(in_dev); } @@ -1253,8 +1250,7 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) rt->u.dst.expires) { unsigned hash = rt_hash_code(rt->fl.fl4_dst, rt->fl.fl4_src ^ - (rt->fl.oif << 5), - rt->fl.fl4_tos); + (rt->fl.oif << 5)); #if RT_CACHE_DEBUG >= 1 printk(KERN_DEBUG "ip_rt_advice: redirect to " "%u.%u.%u.%u/%02x dropped\n", @@ -1391,14 +1387,13 @@ unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu) struct rtable *rth; u32 skeys[2] = { iph->saddr, 0, }; u32 daddr = iph->daddr; - u8 tos = iph->tos & IPTOS_RT_MASK; unsigned short est_mtu = 0; if (ipv4_config.no_pmtu_disc) return 0; for (i = 0; i < 2; i++) { - unsigned hash = rt_hash_code(daddr, skeys[i], tos); + unsigned hash = rt_hash_code(daddr, skeys[i]); rcu_read_lock(); for (rth = rcu_dereference(rt_hash_table[hash].chain); rth; @@ -1407,7 +1402,6 @@ unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu) rth->fl.fl4_src == skeys[i] && rth->rt_dst == daddr && rth->rt_src == iph->saddr && - rth->fl.fl4_tos == tos && rth->fl.iif == 0 && !(dst_metric_locked(&rth->u.dst, RTAX_MTU))) { unsigned short mtu = new_mtu; @@ -1658,7 +1652,7 @@ static int ip_route_input_mc(struct sk_buff *skb, u32 daddr, u32 saddr, RT_CACHE_STAT_INC(in_slow_mc); in_dev_put(in_dev); - hash = rt_hash_code(daddr, saddr ^ (dev->ifindex << 5), tos); + hash = rt_hash_code(daddr, saddr ^ (dev->ifindex << 5)); return rt_intern_hash(hash, rth, (struct rtable**) &skb->dst); e_nobufs: @@ -1823,7 +1817,7 @@ static inline int ip_mkroute_input_def(struct sk_buff *skb, return err; /* put it into the cache */ - hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5), tos); + hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5)); return rt_intern_hash(hash, rth, (struct rtable**)&skb->dst); } @@ -1864,7 +1858,7 @@ static inline int ip_mkroute_input(struct sk_buff *skb, return err; /* put it into the cache */ - hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5), tos); + hash = rt_hash_code(daddr, saddr ^ (fl->iif << 5)); err = rt_intern_hash(hash, rth, &rtres); if (err) return err; @@ -2041,7 +2035,7 @@ local_input: rth->rt_flags &= ~RTCF_LOCAL; } rth->rt_type = res.type; - hash = rt_hash_code(daddr, saddr ^ (fl.iif << 5), tos); + hash = rt_hash_code(daddr, saddr ^ (fl.iif << 5)); err = rt_intern_hash(hash, rth, (struct rtable**)&skb->dst); goto done; @@ -2088,7 +2082,7 @@ int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr, int iif = dev->ifindex; tos &= IPTOS_RT_MASK; - hash = rt_hash_code(daddr, saddr ^ (iif << 5), tos); + hash = rt_hash_code(daddr, saddr ^ (iif << 5)); rcu_read_lock(); for (rth = rcu_dereference(rt_hash_table[hash].chain); rth; @@ -2286,10 +2280,8 @@ static inline int ip_mkroute_output_def(struct rtable **rp, int err = __mkroute_output(&rth, res, fl, oldflp, dev_out, flags); unsigned hash; if (err == 0) { - u32 tos = RT_FL_TOS(oldflp); - hash = rt_hash_code(oldflp->fl4_dst, - oldflp->fl4_src ^ (oldflp->oif << 5), tos); + oldflp->fl4_src ^ (oldflp->oif << 5)); err = rt_intern_hash(hash, rth, rp); } @@ -2304,7 +2296,6 @@ static inline int ip_mkroute_output(struct rtable** rp, unsigned flags) { #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED - u32 tos = RT_FL_TOS(oldflp); unsigned char hop; unsigned hash; int err = -EINVAL; @@ -2334,7 +2325,7 @@ static inline int ip_mkroute_output(struct rtable** rp, hash = rt_hash_code(oldflp->fl4_dst, oldflp->fl4_src ^ - (oldflp->oif << 5), tos); + (oldflp->oif << 5)); err = rt_intern_hash(hash, rth, rp); /* forward hop information to multipath impl. */ @@ -2563,7 +2554,7 @@ int __ip_route_output_key(struct rtable **rp, const struct flowi *flp) unsigned hash; struct rtable *rth; - hash = rt_hash_code(flp->fl4_dst, flp->fl4_src ^ (flp->oif << 5), flp->fl4_tos); + hash = rt_hash_code(flp->fl4_dst, flp->fl4_src ^ (flp->oif << 5)); rcu_read_lock_bh(); for (rth = rcu_dereference(rt_hash_table[hash].chain); rth; -- cgit v1.2.3 From c08b8a49100715b20e6f7c997e992428b5e06078 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 25 Mar 2006 03:06:33 -0800 Subject: [PATCH] sys_alarm() unsigned signed conversion fixup alarm() calls the kernel with an unsigend int timeout in seconds. The value is stored in the tv_sec field of a struct timeval to setup the itimer. The tv_sec field of struct timeval is of type long, which causes the tv_sec value to be negative on 32 bit machines if seconds > INT_MAX. Before the hrtimer merge (pre 2.6.16) such a negative value was converted to the maximum jiffies timeout by the timeval_to_jiffies conversion. It's not clear whether this was intended or just happened to be done by the timeval_to_jiffies code. hrtimers expect a timeval in canonical form and treat a negative timeout as already expired. This breaks the legitimate usage of alarm() with a timeout value > INT_MAX seconds. For 32 bit machines it is therefor necessary to limit the internal seconds value to avoid API breakage. Instead of doing this in all implementations of sys_alarm the duplicated sys_alarm code is moved into a common function in itimer.c Signed-off-by: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/ia32/sys_ia32.c | 14 +------------- arch/mips/kernel/sysirix.c | 22 +--------------------- arch/x86_64/ia32/sys_ia32.c | 16 ++-------------- include/linux/time.h | 1 + kernel/itimer.c | 37 +++++++++++++++++++++++++++++++++++++ kernel/timer.c | 14 +------------- 6 files changed, 43 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 70dba1f0e2ee..13e739e4c84d 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -1166,19 +1166,7 @@ put_tv32 (struct compat_timeval __user *o, struct timeval *i) asmlinkage unsigned long sys32_alarm (unsigned int seconds) { - struct itimerval it_new, it_old; - unsigned int oldalarm; - - it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; - it_new.it_value.tv_sec = seconds; - it_new.it_value.tv_usec = 0; - do_setitimer(ITIMER_REAL, &it_new, &it_old); - oldalarm = it_old.it_value.tv_sec; - /* ehhh.. We can't return 0 if we have an alarm pending.. */ - /* And we'd better return too much than too little anyway */ - if (it_old.it_value.tv_usec) - oldalarm++; - return oldalarm; + return alarm_setitimer(seconds); } /* Translations due to time_t size differences. Which affects all diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c index 0fc3730a294f..5407b784cd01 100644 --- a/arch/mips/kernel/sysirix.c +++ b/arch/mips/kernel/sysirix.c @@ -645,27 +645,7 @@ static inline void getitimer_real(struct itimerval *value) asmlinkage unsigned int irix_alarm(unsigned int seconds) { - struct itimerval it_new, it_old; - unsigned int oldalarm; - - if (!seconds) { - getitimer_real(&it_old); - del_timer(¤t->real_timer); - } else { - it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; - it_new.it_value.tv_sec = seconds; - it_new.it_value.tv_usec = 0; - do_setitimer(ITIMER_REAL, &it_new, &it_old); - } - oldalarm = it_old.it_value.tv_sec; - /* - * ehhh.. We can't return 0 if we have an alarm pending ... - * And we'd better return too much than too little anyway - */ - if (it_old.it_value.tv_usec) - oldalarm++; - - return oldalarm; + return alarm_setitimer(seconds); } asmlinkage int irix_pause(void) diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c index 2bc55af95419..2b2d029f477c 100644 --- a/arch/x86_64/ia32/sys_ia32.c +++ b/arch/x86_64/ia32/sys_ia32.c @@ -430,24 +430,12 @@ put_tv32(struct compat_timeval __user *o, struct timeval *i) return err; } -extern int do_setitimer(int which, struct itimerval *, struct itimerval *); +extern unsigned int alarm_setitimer(unsigned int seconds); asmlinkage long sys32_alarm(unsigned int seconds) { - struct itimerval it_new, it_old; - unsigned int oldalarm; - - it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; - it_new.it_value.tv_sec = seconds; - it_new.it_value.tv_usec = 0; - do_setitimer(ITIMER_REAL, &it_new, &it_old); - oldalarm = it_old.it_value.tv_sec; - /* ehhh.. We can't return 0 if we have an alarm pending.. */ - /* And we'd better return too much than too little anyway */ - if (it_old.it_value.tv_usec) - oldalarm++; - return oldalarm; + return alarm_setitimer(seconds); } /* Translations due to time_t size differences. Which affects all diff --git a/include/linux/time.h b/include/linux/time.h index d9cdba54b789..bf0e785e2e03 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -101,6 +101,7 @@ extern long do_utimes(int dfd, char __user *filename, struct timeval *times); struct itimerval; extern int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue); +extern unsigned int alarm_setitimer(unsigned int seconds); extern int do_getitimer(int which, struct itimerval *value); extern void getnstimeofday(struct timespec *tv); diff --git a/kernel/itimer.c b/kernel/itimer.c index 379be2f8c84c..a2dc375927d8 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -226,6 +226,43 @@ again: return 0; } +/** + * alarm_setitimer - set alarm in seconds + * + * @seconds: number of seconds until alarm + * 0 disables the alarm + * + * Returns the remaining time in seconds of a pending timer or 0 when + * the timer is not active. + * + * On 32 bit machines the seconds value is limited to (INT_MAX/2) to avoid + * negative timeval settings which would cause immediate expiry. + */ +unsigned int alarm_setitimer(unsigned int seconds) +{ + struct itimerval it_new, it_old; + +#if BITS_PER_LONG < 64 + if (seconds > INT_MAX) + seconds = INT_MAX; +#endif + it_new.it_value.tv_sec = seconds; + it_new.it_value.tv_usec = 0; + it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; + + do_setitimer(ITIMER_REAL, &it_new, &it_old); + + /* + * We can't return 0 if we have an alarm pending ... And we'd + * better return too much than too little anyway + */ + if ((!it_old.it_value.tv_sec && it_old.it_value.tv_usec) || + it_old.it_value.tv_usec >= 500000) + it_old.it_value.tv_sec++; + + return it_old.it_value.tv_sec; +} + asmlinkage long sys_setitimer(int which, struct itimerval __user *value, struct itimerval __user *ovalue) diff --git a/kernel/timer.c b/kernel/timer.c index 17d956cebcb9..13fa72cac7d8 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -956,19 +956,7 @@ void do_timer(struct pt_regs *regs) */ asmlinkage unsigned long sys_alarm(unsigned int seconds) { - struct itimerval it_new, it_old; - unsigned int oldalarm; - - it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; - it_new.it_value.tv_sec = seconds; - it_new.it_value.tv_usec = 0; - do_setitimer(ITIMER_REAL, &it_new, &it_old); - oldalarm = it_old.it_value.tv_sec; - /* ehhh.. We can't return 0 if we have an alarm pending.. */ - /* And we'd better return too much than too little anyway */ - if ((!oldalarm && it_old.it_value.tv_usec) || it_old.it_value.tv_usec >= 500000) - oldalarm++; - return oldalarm; + return alarm_setitimer(seconds); } #endif -- cgit v1.2.3 From 871751e25d956ad24f129ca972b7851feaa61d53 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 25 Mar 2006 03:06:39 -0800 Subject: [PATCH] slab: implement /proc/slab_allocators Implement /proc/slab_allocators. It produces output like: idr_layer_cache: 80 idr_pre_get+0x33/0x4e buffer_head: 2555 alloc_buffer_head+0x20/0x75 mm_struct: 9 mm_alloc+0x1e/0x42 mm_struct: 20 dup_mm+0x36/0x370 vm_area_struct: 384 dup_mm+0x18f/0x370 vm_area_struct: 151 do_mmap_pgoff+0x2e0/0x7c3 vm_area_struct: 1 split_vma+0x5a/0x10e vm_area_struct: 11 do_brk+0x206/0x2e2 vm_area_struct: 2 copy_vma+0xda/0x142 vm_area_struct: 9 setup_arg_pages+0x99/0x214 fs_cache: 8 copy_fs_struct+0x21/0x133 fs_cache: 29 copy_process+0xf38/0x10e3 files_cache: 30 alloc_files+0x1b/0xcf signal_cache: 81 copy_process+0xbaa/0x10e3 sighand_cache: 77 copy_process+0xe65/0x10e3 sighand_cache: 1 de_thread+0x4d/0x5f8 anon_vma: 241 anon_vma_prepare+0xd9/0xf3 size-2048: 1 add_sect_attrs+0x5f/0x145 size-2048: 2 journal_init_revoke+0x99/0x302 size-2048: 2 journal_init_revoke+0x137/0x302 size-2048: 2 journal_init_inode+0xf9/0x1c4 Cc: Manfred Spraul Cc: Alexander Nyberg Cc: Pekka Enberg Cc: Christoph Lameter Cc: Ravikiran Thirumalai Signed-off-by: Al Viro DESC slab-leaks3-locking-fix EDESC From: Andrew Morton Update for slab-remove-cachep-spinlock.patch Cc: Al Viro Cc: Manfred Spraul Cc: Alexander Nyberg Cc: Pekka Enberg Cc: Christoph Lameter Cc: Ravikiran Thirumalai Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/proc_misc.c | 37 +++++++++++ include/linux/slab.h | 6 +- lib/Kconfig.debug | 4 ++ mm/slab.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++-- mm/util.c | 4 +- net/core/skbuff.c | 2 +- 6 files changed, 222 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 826c131994c3..1e9ea37d457e 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -485,6 +485,40 @@ static struct file_operations proc_slabinfo_operations = { .llseek = seq_lseek, .release = seq_release, }; + +#ifdef CONFIG_DEBUG_SLAB_LEAK +extern struct seq_operations slabstats_op; +static int slabstats_open(struct inode *inode, struct file *file) +{ + unsigned long *n = kzalloc(PAGE_SIZE, GFP_KERNEL); + int ret = -ENOMEM; + if (n) { + ret = seq_open(file, &slabstats_op); + if (!ret) { + struct seq_file *m = file->private_data; + *n = PAGE_SIZE / (2 * sizeof(unsigned long)); + m->private = n; + n = NULL; + } + kfree(n); + } + return ret; +} + +static int slabstats_release(struct inode *inode, struct file *file) +{ + struct seq_file *m = file->private_data; + kfree(m->private); + return seq_release(inode, file); +} + +static struct file_operations proc_slabstats_operations = { + .open = slabstats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = slabstats_release, +}; +#endif #endif static int show_stat(struct seq_file *p, void *v) @@ -744,6 +778,9 @@ void __init proc_misc_init(void) create_seq_entry("interrupts", 0, &proc_interrupts_operations); #ifdef CONFIG_SLAB create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations); +#ifdef CONFIG_DEBUG_SLAB_LEAK + create_seq_entry("slab_allocators", 0 ,&proc_slabstats_operations); +#endif #endif create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations); create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations); diff --git a/include/linux/slab.h b/include/linux/slab.h index e2ee5b268797..f88e08a5802c 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -77,11 +77,12 @@ struct cache_sizes { }; extern struct cache_sizes malloc_sizes[]; -#ifndef CONFIG_DEBUG_SLAB extern void *__kmalloc(size_t, gfp_t); +#ifndef CONFIG_DEBUG_SLAB +#define ____kmalloc(size, flags) __kmalloc(size, flags) #else extern void *__kmalloc_track_caller(size_t, gfp_t, void*); -#define __kmalloc(size, flags) \ +#define ____kmalloc(size, flags) \ __kmalloc_track_caller(size, flags, __builtin_return_address(0)) #endif @@ -173,6 +174,7 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags) #define kmem_ptr_validate(a, b) (0) #define kmem_cache_alloc_node(c, f, n) kmem_cache_alloc(c, f) #define kmalloc_node(s, f, n) kmalloc(s, f) +#define ____kmalloc kmalloc #endif /* CONFIG_SLOB */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index f2618e1c2b93..1fe3f897145f 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -85,6 +85,10 @@ config DEBUG_SLAB allocation as well as poisoning memory on free to catch use of freed memory. This can make kmalloc/kfree-intensive workloads much slower. +config DEBUG_SLAB_LEAK + bool "Memory leak debugging" + depends on DEBUG_SLAB + config DEBUG_PREEMPT bool "Debug preemptible kernel" depends on DEBUG_KERNEL && PREEMPT diff --git a/mm/slab.c b/mm/slab.c index 26138c9f8f00..a5047161084e 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -204,7 +204,8 @@ typedef unsigned int kmem_bufctl_t; #define BUFCTL_END (((kmem_bufctl_t)(~0U))-0) #define BUFCTL_FREE (((kmem_bufctl_t)(~0U))-1) -#define SLAB_LIMIT (((kmem_bufctl_t)(~0U))-2) +#define BUFCTL_ACTIVE (((kmem_bufctl_t)(~0U))-2) +#define SLAB_LIMIT (((kmem_bufctl_t)(~0U))-3) /* Max number of objs-per-slab for caches which use off-slab slabs. * Needed to avoid a possible looping condition in cache_grow(). @@ -2399,7 +2400,7 @@ static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp, /* Verify that the slab belongs to the intended node */ WARN_ON(slabp->nodeid != nodeid); - if (slab_bufctl(slabp)[objnr] != BUFCTL_FREE) { + if (slab_bufctl(slabp)[objnr] + 1 <= SLAB_LIMIT + 1) { printk(KERN_ERR "slab: double free detected in cache " "'%s', objp %p\n", cachep->name, objp); BUG(); @@ -2605,6 +2606,9 @@ static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp, */ cachep->dtor(objp + obj_offset(cachep), cachep, 0); } +#ifdef CONFIG_DEBUG_SLAB_LEAK + slab_bufctl(slabp)[objnr] = BUFCTL_FREE; +#endif if (cachep->flags & SLAB_POISON) { #ifdef CONFIG_DEBUG_PAGEALLOC if ((cachep->buffer_size % PAGE_SIZE)==0 && OFF_SLAB(cachep)) { @@ -2788,6 +2792,16 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep, *dbg_redzone1(cachep, objp) = RED_ACTIVE; *dbg_redzone2(cachep, objp) = RED_ACTIVE; } +#ifdef CONFIG_DEBUG_SLAB_LEAK + { + struct slab *slabp; + unsigned objnr; + + slabp = page_get_slab(virt_to_page(objp)); + objnr = (unsigned)(objp - slabp->s_mem) / cachep->buffer_size; + slab_bufctl(slabp)[objnr] = BUFCTL_ACTIVE; + } +#endif objp += obj_offset(cachep); if (cachep->ctor && cachep->flags & SLAB_POISON) { unsigned long ctor_flags = SLAB_CTOR_CONSTRUCTOR; @@ -3220,22 +3234,23 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, return __cache_alloc(cachep, flags, caller); } -#ifndef CONFIG_DEBUG_SLAB void *__kmalloc(size_t size, gfp_t flags) { +#ifndef CONFIG_DEBUG_SLAB return __do_kmalloc(size, flags, NULL); +#else + return __do_kmalloc(size, flags, __builtin_return_address(0)); +#endif } EXPORT_SYMBOL(__kmalloc); -#else - +#ifdef CONFIG_DEBUG_SLAB void *__kmalloc_track_caller(size_t size, gfp_t flags, void *caller) { return __do_kmalloc(size, flags, caller); } EXPORT_SYMBOL(__kmalloc_track_caller); - #endif #ifdef CONFIG_SMP @@ -3899,6 +3914,159 @@ ssize_t slabinfo_write(struct file *file, const char __user * buffer, res = count; return res; } + +#ifdef CONFIG_DEBUG_SLAB_LEAK + +static void *leaks_start(struct seq_file *m, loff_t *pos) +{ + loff_t n = *pos; + struct list_head *p; + + mutex_lock(&cache_chain_mutex); + p = cache_chain.next; + while (n--) { + p = p->next; + if (p == &cache_chain) + return NULL; + } + return list_entry(p, struct kmem_cache, next); +} + +static inline int add_caller(unsigned long *n, unsigned long v) +{ + unsigned long *p; + int l; + if (!v) + return 1; + l = n[1]; + p = n + 2; + while (l) { + int i = l/2; + unsigned long *q = p + 2 * i; + if (*q == v) { + q[1]++; + return 1; + } + if (*q > v) { + l = i; + } else { + p = q + 2; + l -= i + 1; + } + } + if (++n[1] == n[0]) + return 0; + memmove(p + 2, p, n[1] * 2 * sizeof(unsigned long) - ((void *)p - (void *)n)); + p[0] = v; + p[1] = 1; + return 1; +} + +static void handle_slab(unsigned long *n, struct kmem_cache *c, struct slab *s) +{ + void *p; + int i; + if (n[0] == n[1]) + return; + for (i = 0, p = s->s_mem; i < c->num; i++, p += c->buffer_size) { + if (slab_bufctl(s)[i] != BUFCTL_ACTIVE) + continue; + if (!add_caller(n, (unsigned long)*dbg_userword(c, p))) + return; + } +} + +static void show_symbol(struct seq_file *m, unsigned long address) +{ +#ifdef CONFIG_KALLSYMS + char *modname; + const char *name; + unsigned long offset, size; + char namebuf[KSYM_NAME_LEN+1]; + + name = kallsyms_lookup(address, &size, &offset, &modname, namebuf); + + if (name) { + seq_printf(m, "%s+%#lx/%#lx", name, offset, size); + if (modname) + seq_printf(m, " [%s]", modname); + return; + } +#endif + seq_printf(m, "%p", (void *)address); +} + +static int leaks_show(struct seq_file *m, void *p) +{ + struct kmem_cache *cachep = p; + struct list_head *q; + struct slab *slabp; + struct kmem_list3 *l3; + const char *name; + unsigned long *n = m->private; + int node; + int i; + + if (!(cachep->flags & SLAB_STORE_USER)) + return 0; + if (!(cachep->flags & SLAB_RED_ZONE)) + return 0; + + /* OK, we can do it */ + + n[1] = 0; + + for_each_online_node(node) { + l3 = cachep->nodelists[node]; + if (!l3) + continue; + + check_irq_on(); + spin_lock_irq(&l3->list_lock); + + list_for_each(q, &l3->slabs_full) { + slabp = list_entry(q, struct slab, list); + handle_slab(n, cachep, slabp); + } + list_for_each(q, &l3->slabs_partial) { + slabp = list_entry(q, struct slab, list); + handle_slab(n, cachep, slabp); + } + spin_unlock_irq(&l3->list_lock); + } + name = cachep->name; + if (n[0] == n[1]) { + /* Increase the buffer size */ + mutex_unlock(&cache_chain_mutex); + m->private = kzalloc(n[0] * 4 * sizeof(unsigned long), GFP_KERNEL); + if (!m->private) { + /* Too bad, we are really out */ + m->private = n; + mutex_lock(&cache_chain_mutex); + return -ENOMEM; + } + *(unsigned long *)m->private = n[0] * 2; + kfree(n); + mutex_lock(&cache_chain_mutex); + /* Now make sure this entry will be retried */ + m->count = m->size; + return 0; + } + for (i = 0; i < n[1]; i++) { + seq_printf(m, "%s: %lu ", name, n[2*i+3]); + show_symbol(m, n[2*i+2]); + seq_putc(m, '\n'); + } + return 0; +} + +struct seq_operations slabstats_op = { + .start = leaks_start, + .next = s_next, + .stop = s_stop, + .show = leaks_show, +}; +#endif #endif /** diff --git a/mm/util.c b/mm/util.c index 49e29f751b50..b68d3d7d0359 100644 --- a/mm/util.c +++ b/mm/util.c @@ -11,7 +11,7 @@ */ void *kzalloc(size_t size, gfp_t flags) { - void *ret = kmalloc(size, flags); + void *ret = ____kmalloc(size, flags); if (ret) memset(ret, 0, size); return ret; @@ -33,7 +33,7 @@ char *kstrdup(const char *s, gfp_t gfp) return NULL; len = strlen(s) + 1; - buf = kmalloc(len, gfp); + buf = ____kmalloc(len, gfp); if (buf) memcpy(buf, s, len); return buf; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index c9f878454531..09464fa8d72f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -149,7 +149,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, /* Get the DATA. Size must match skb_add_mtu(). */ size = SKB_DATA_ALIGN(size); - data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); + data = ____kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); if (!data) goto nodata; -- cgit v1.2.3 From a8c0f9a41f88da703ade33f9c1626a55c786e8bb Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sat, 25 Mar 2006 03:06:42 -0800 Subject: [PATCH] slab: introduce kmem_cache_zalloc allocator Introduce a memory-zeroing variant of kmem_cache_alloc. The allocator already exits in XFS and there are potential users for it so this patch makes the allocator available for the general public. Signed-off-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/slab.h | 2 ++ mm/slab.c | 17 +++++++++++++++++ mm/slob.c | 10 ++++++++++ 3 files changed, 29 insertions(+) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index f88e08a5802c..1216b09e07b1 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -64,6 +64,7 @@ extern kmem_cache_t *kmem_cache_create(const char *, size_t, size_t, unsigned lo extern int kmem_cache_destroy(kmem_cache_t *); extern int kmem_cache_shrink(kmem_cache_t *); extern void *kmem_cache_alloc(kmem_cache_t *, gfp_t); +extern void *kmem_cache_zalloc(struct kmem_cache *, gfp_t); extern void kmem_cache_free(kmem_cache_t *, void *); extern unsigned int kmem_cache_size(kmem_cache_t *); extern const char *kmem_cache_name(kmem_cache_t *); @@ -156,6 +157,7 @@ struct kmem_cache *kmem_cache_create(const char *c, size_t, size_t, void (*)(void *, struct kmem_cache *, unsigned long)); int kmem_cache_destroy(struct kmem_cache *c); void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags); +void *kmem_cache_zalloc(struct kmem_cache *, gfp_t); void kmem_cache_free(struct kmem_cache *c, void *b); const char *kmem_cache_name(struct kmem_cache *); void *kmalloc(size_t size, gfp_t flags); diff --git a/mm/slab.c b/mm/slab.c index a5047161084e..6f5aeebd4306 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3107,6 +3107,23 @@ void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) } EXPORT_SYMBOL(kmem_cache_alloc); +/** + * kmem_cache_alloc - Allocate an object. The memory is set to zero. + * @cache: The cache to allocate from. + * @flags: See kmalloc(). + * + * Allocate an object from this cache and set the allocated memory to zero. + * The flags are only relevant if the cache has no available objects. + */ +void *kmem_cache_zalloc(struct kmem_cache *cache, gfp_t flags) +{ + void *ret = __cache_alloc(cache, flags, __builtin_return_address(0)); + if (ret) + memset(ret, 0, obj_size(cache)); + return ret; +} +EXPORT_SYMBOL(kmem_cache_zalloc); + /** * kmem_ptr_validate - check if an untrusted pointer might * be a slab entry. diff --git a/mm/slob.c b/mm/slob.c index a1f42bdc0245..9bcc7e2cabfd 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -294,6 +294,16 @@ void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags) } EXPORT_SYMBOL(kmem_cache_alloc); +void *kmem_cache_zalloc(struct kmem_cache *c, gfp_t flags) +{ + void *ret = kmem_cache_alloc(c, flags); + if (ret) + memset(ret, 0, c->size); + + return ret; +} +EXPORT_SYMBOL(kmem_cache_zalloc); + void kmem_cache_free(struct kmem_cache *c, void *b) { if (c->dtor) -- cgit v1.2.3 From 40c07ae8daa659b8feb149c84731629386873c16 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sat, 25 Mar 2006 03:06:43 -0800 Subject: [PATCH] slab: optimize constant-size kzalloc calls As suggested by Eric Dumazet, optimize kzalloc() calls that pass a compile-time constant size. Please note that the patch increases kernel text slightly (~200 bytes for defconfig on x86). Signed-off-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/slab.h | 30 +++++++++++++++++++++++++++--- mm/util.c | 6 +++--- 2 files changed, 30 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index 1216b09e07b1..15e1d9736b1b 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -110,7 +110,30 @@ found: return __kmalloc(size, flags); } -extern void *kzalloc(size_t, gfp_t); +extern void *__kzalloc(size_t, gfp_t); + +static inline void *kzalloc(size_t size, gfp_t flags) +{ + if (__builtin_constant_p(size)) { + int i = 0; +#define CACHE(x) \ + if (size <= x) \ + goto found; \ + else \ + i++; +#include "kmalloc_sizes.h" +#undef CACHE + { + extern void __you_cannot_kzalloc_that_much(void); + __you_cannot_kzalloc_that_much(); + } +found: + return kmem_cache_zalloc((flags & GFP_DMA) ? + malloc_sizes[i].cs_dmacachep : + malloc_sizes[i].cs_cachep, flags); + } + return __kzalloc(size, flags); +} /** * kcalloc - allocate memory for an array. The memory is set to zero. @@ -161,14 +184,14 @@ void *kmem_cache_zalloc(struct kmem_cache *, gfp_t); void kmem_cache_free(struct kmem_cache *c, void *b); const char *kmem_cache_name(struct kmem_cache *); void *kmalloc(size_t size, gfp_t flags); -void *kzalloc(size_t size, gfp_t flags); +void *__kzalloc(size_t size, gfp_t flags); void kfree(const void *m); unsigned int ksize(const void *m); unsigned int kmem_cache_size(struct kmem_cache *c); static inline void *kcalloc(size_t n, size_t size, gfp_t flags) { - return kzalloc(n * size, flags); + return __kzalloc(n * size, flags); } #define kmem_cache_shrink(d) (0) @@ -176,6 +199,7 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags) #define kmem_ptr_validate(a, b) (0) #define kmem_cache_alloc_node(c, f, n) kmem_cache_alloc(c, f) #define kmalloc_node(s, f, n) kmalloc(s, f) +#define kzalloc(s, f) __kzalloc(s, f) #define ____kmalloc kmalloc #endif /* CONFIG_SLOB */ diff --git a/mm/util.c b/mm/util.c index b68d3d7d0359..7368479220b3 100644 --- a/mm/util.c +++ b/mm/util.c @@ -5,18 +5,18 @@ #include /** - * kzalloc - allocate memory. The memory is set to zero. + * __kzalloc - allocate memory. The memory is set to zero. * @size: how many bytes of memory are required. * @flags: the type of memory to allocate. */ -void *kzalloc(size_t size, gfp_t flags) +void *__kzalloc(size_t size, gfp_t flags) { void *ret = ____kmalloc(size, flags); if (ret) memset(ret, 0, size); return ret; } -EXPORT_SYMBOL(kzalloc); +EXPORT_SYMBOL(__kzalloc); /* * kstrdup - allocate space for and copy an existing string -- cgit v1.2.3 From e3df18983ea090a2e00dd5c2c6167bb431a0e0a2 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 25 Mar 2006 03:06:53 -0800 Subject: [PATCH] jbd: embed j_commit_timer in journal struct The kjournald timer is currently on the kernel thread's stack and the journal structure points at it. Save a pointer hop by moving the timer into the journal structure. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/jbd/journal.c | 19 +++++++++---------- fs/jbd/transaction.c | 4 ++-- include/linux/jbd.h | 4 +++- 3 files changed, 14 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c index 95a628d8cac8..6bd4647dc5a1 100644 --- a/fs/jbd/journal.c +++ b/fs/jbd/journal.c @@ -111,18 +111,17 @@ static void commit_timeout(unsigned long __data) static int kjournald(void *arg) { - journal_t *journal = (journal_t *) arg; + journal_t *journal = arg; transaction_t *transaction; - struct timer_list timer; daemonize("kjournald"); - /* Set up an interval timer which can be used to trigger a - commit wakeup after the commit interval expires */ - init_timer(&timer); - timer.data = (unsigned long) current; - timer.function = commit_timeout; - journal->j_commit_timer = &timer; + /* + * Set up an interval timer which can be used to trigger a commit wakeup + * after the commit interval expires + */ + setup_timer(&journal->j_commit_timer, commit_timeout, + (unsigned long)current); /* Record that the journal thread is running */ journal->j_task = current; @@ -146,7 +145,7 @@ loop: if (journal->j_commit_sequence != journal->j_commit_request) { jbd_debug(1, "OK, requests differ\n"); spin_unlock(&journal->j_state_lock); - del_timer_sync(journal->j_commit_timer); + del_timer_sync(&journal->j_commit_timer); journal_commit_transaction(journal); spin_lock(&journal->j_state_lock); goto loop; @@ -203,7 +202,7 @@ loop: end_loop: spin_unlock(&journal->j_state_lock); - del_timer_sync(journal->j_commit_timer); + del_timer_sync(&journal->j_commit_timer); journal->j_task = NULL; wake_up(&journal->j_wait_done_commit); jbd_debug(1, "Journal thread exiting.\n"); diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index 5fc40888f4cf..ada31fa272e3 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -53,8 +53,8 @@ get_transaction(journal_t *journal, transaction_t *transaction) spin_lock_init(&transaction->t_handle_lock); /* Set up the commit timer for the new transaction. */ - journal->j_commit_timer->expires = transaction->t_expires; - add_timer(journal->j_commit_timer); + journal->j_commit_timer.expires = transaction->t_expires; + add_timer(&journal->j_commit_timer); J_ASSERT(journal->j_running_transaction == NULL); journal->j_running_transaction = transaction; diff --git a/include/linux/jbd.h b/include/linux/jbd.h index 2ccbfb6340ba..4fc7dffd66ef 100644 --- a/include/linux/jbd.h +++ b/include/linux/jbd.h @@ -29,6 +29,8 @@ #include #include #include +#include + #include #endif @@ -787,7 +789,7 @@ struct journal_s unsigned long j_commit_interval; /* The timer used to wakeup the commit thread: */ - struct timer_list *j_commit_timer; + struct timer_list j_commit_timer; /* * The revoke table: maintains the list of revoked blocks in the -- cgit v1.2.3 From ca5734db60630f7c5564a61a5b9034c1bb369c3d Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Sat, 25 Mar 2006 03:06:55 -0800 Subject: [PATCH] Small cleanup in quota.h Remove unused quota flag. Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/quota.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/quota.h b/include/linux/quota.h index 8dc2d04a103f..2dab71e1c3d1 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -209,7 +209,6 @@ extern struct dqstats dqstats; #define DQ_FAKE_B 3 /* no limits only usage */ #define DQ_READ_B 4 /* dquot was read into memory */ #define DQ_ACTIVE_B 5 /* dquot is active (dquot_release not called) */ -#define DQ_WAITFREE_B 6 /* dquot being waited (by invalidate_dquots) */ struct dquot { struct hlist_node dq_hash; /* Hash list in memory */ -- cgit v1.2.3 From bdfc326614b90e7bc47ee4a8fed05988555f0169 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sat, 25 Mar 2006 03:06:56 -0800 Subject: [PATCH] fs/inode.c: make iprune_mutex static There's no reason for iprune_mutex being global. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/inode.c | 2 +- include/linux/fs.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/fs/inode.c b/fs/inode.c index a51c671c54cf..85da11044adc 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -91,7 +91,7 @@ DEFINE_SPINLOCK(inode_lock); * from its final dispose_list, the struct super_block they refer to * (for inode->i_sb->s_op) may already have been freed and reused. */ -DEFINE_MUTEX(iprune_mutex); +static DEFINE_MUTEX(iprune_mutex); /* * Statistics gathering.. diff --git a/include/linux/fs.h b/include/linux/fs.h index 215696a0f16f..7c750312261b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1558,7 +1558,6 @@ extern void destroy_inode(struct inode *); extern struct inode *new_inode(struct super_block *); extern int remove_suid(struct dentry *); extern void remove_dquot_ref(struct super_block *, int, struct list_head *); -extern struct mutex iprune_mutex; extern void __insert_inode_hash(struct inode *, unsigned long hashval); extern void remove_inode_hash(struct inode *); -- cgit v1.2.3 From 23f9e0f891c9b159a199629d4426f6ae0c383508 Mon Sep 17 00:00:00 2001 From: Alexander Zarochentzev Date: Sat, 25 Mar 2006 03:06:57 -0800 Subject: [PATCH] reiserfs: fix transaction overflowing This patch fixes a bug in reiserfs truncate. A transaction might overflow when truncating long highly fragmented file. The fix is to split truncation into several transactions to avoid overflowing. Signed-off-by: Vladimir V. Saveliev Cc; Charles McColgan Cc: Alexander Zarochentsev Cc: Hans Reiser Cc: Chris Mason Cc: Jeff Mahoney Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/stree.c | 210 +++++++++++++++----------------------------- include/linux/reiserfs_fs.h | 5 ++ 2 files changed, 77 insertions(+), 138 deletions(-) (limited to 'include') diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c index e2d08d7bcffc..d2b25e1ba6e9 100644 --- a/fs/reiserfs/stree.c +++ b/fs/reiserfs/stree.c @@ -981,6 +981,8 @@ static inline int prepare_for_direntry_item(struct path *path, return M_CUT; } +#define JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD (2 * JOURNAL_PER_BALANCE_CNT + 1) + /* If the path points to a directory or direct item, calculate mode and the size cut, for balance. If the path points to an indirect item, remove some number of its unformatted nodes. In case of file truncate calculate whether this item must be deleted/truncated or last @@ -1020,148 +1022,79 @@ static char prepare_for_delete_or_cut(struct reiserfs_transaction_handle *th, st /* Case of an indirect item. */ { - int n_unfm_number, /* Number of the item unformatted nodes. */ - n_counter, n_blk_size; - __le32 *p_n_unfm_pointer; /* Pointer to the unformatted node number. */ - __u32 tmp; - struct item_head s_ih; /* Item header. */ - char c_mode; /* Returned mode of the balance. */ - int need_research; - - n_blk_size = p_s_sb->s_blocksize; - - /* Search for the needed object indirect item until there are no unformatted nodes to be removed. */ - do { - need_research = 0; - p_s_bh = PATH_PLAST_BUFFER(p_s_path); - /* Copy indirect item header to a temp variable. */ - copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path)); - /* Calculate number of unformatted nodes in this item. */ - n_unfm_number = I_UNFM_NUM(&s_ih); - - RFALSE(!is_indirect_le_ih(&s_ih) || !n_unfm_number || - pos_in_item(p_s_path) + 1 != n_unfm_number, - "PAP-5240: invalid item %h " - "n_unfm_number = %d *p_n_pos_in_item = %d", - &s_ih, n_unfm_number, pos_in_item(p_s_path)); - - /* Calculate balance mode and position in the item to remove unformatted nodes. */ - if (n_new_file_length == max_reiserfs_offset(inode)) { /* Case of delete. */ - pos_in_item(p_s_path) = 0; - *p_n_cut_size = -(IH_SIZE + ih_item_len(&s_ih)); - c_mode = M_DELETE; - } else { /* Case of truncate. */ - if (n_new_file_length < le_ih_k_offset(&s_ih)) { - pos_in_item(p_s_path) = 0; - *p_n_cut_size = - -(IH_SIZE + ih_item_len(&s_ih)); - c_mode = M_DELETE; /* Delete this item. */ - } else { - /* indirect item must be truncated starting from *p_n_pos_in_item-th position */ - pos_in_item(p_s_path) = - (n_new_file_length + n_blk_size - - le_ih_k_offset(&s_ih)) >> p_s_sb-> - s_blocksize_bits; - - RFALSE(pos_in_item(p_s_path) > - n_unfm_number, - "PAP-5250: invalid position in the item"); - - /* Either convert last unformatted node of indirect item to direct item or increase - its free space. */ - if (pos_in_item(p_s_path) == - n_unfm_number) { - *p_n_cut_size = 0; /* Nothing to cut. */ - return M_CONVERT; /* Maybe convert last unformatted node to the direct item. */ - } - /* Calculate size to cut. */ - *p_n_cut_size = - -(ih_item_len(&s_ih) - - pos_in_item(p_s_path) * - UNFM_P_SIZE); - - c_mode = M_CUT; /* Cut from this indirect item. */ - } - } + int blk_size = p_s_sb->s_blocksize; + struct item_head s_ih; + int need_re_search; + int delete = 0; + int result = M_CUT; + int pos = 0; + + if ( n_new_file_length == max_reiserfs_offset (inode) ) { + /* prepare_for_delete_or_cut() is called by + * reiserfs_delete_item() */ + n_new_file_length = 0; + delete = 1; + } + + do { + need_re_search = 0; + *p_n_cut_size = 0; + p_s_bh = PATH_PLAST_BUFFER(p_s_path); + copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path)); + pos = I_UNFM_NUM(&s_ih); - RFALSE(n_unfm_number <= pos_in_item(p_s_path), - "PAP-5260: invalid position in the indirect item"); - - /* pointers to be cut */ - n_unfm_number -= pos_in_item(p_s_path); - /* Set pointer to the last unformatted node pointer that is to be cut. */ - p_n_unfm_pointer = - (__le32 *) B_I_PITEM(p_s_bh, - &s_ih) + I_UNFM_NUM(&s_ih) - - 1 - *p_n_removed; - - /* We go through the unformatted nodes pointers of the indirect - item and look for the unformatted nodes in the cache. If we - found some of them we free it, zero corresponding indirect item - entry and log buffer containing that indirect item. For this we - need to prepare last path element for logging. If some - unformatted node has b_count > 1 we must not free this - unformatted node since it is in use. */ - reiserfs_prepare_for_journal(p_s_sb, p_s_bh, 1); - // note: path could be changed, first line in for loop takes care - // of it + while (le_ih_k_offset (&s_ih) + (pos - 1) * blk_size > n_new_file_length) { + __u32 *unfm, block; - for (n_counter = *p_n_removed; - n_counter < n_unfm_number; - n_counter++, p_n_unfm_pointer--) { + /* Each unformatted block deletion may involve one additional + * bitmap block into the transaction, thereby the initial + * journal space reservation might not be enough. */ + if (!delete && (*p_n_cut_size) != 0 && + reiserfs_transaction_free_space(th) < JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD) { + break; + } - cond_resched(); - if (item_moved(&s_ih, p_s_path)) { - need_research = 1; - break; - } - RFALSE(p_n_unfm_pointer < - (__le32 *) B_I_PITEM(p_s_bh, &s_ih) - || p_n_unfm_pointer > - (__le32 *) B_I_PITEM(p_s_bh, - &s_ih) + - I_UNFM_NUM(&s_ih) - 1, - "vs-5265: pointer out of range"); - - /* Hole, nothing to remove. */ - if (!get_block_num(p_n_unfm_pointer, 0)) { - (*p_n_removed)++; - continue; - } + unfm = (__u32 *)B_I_PITEM(p_s_bh, &s_ih) + pos - 1; + block = get_block_num(unfm, 0); - (*p_n_removed)++; + if (block != 0) { + reiserfs_prepare_for_journal(p_s_sb, p_s_bh, 1); + put_block_num(unfm, 0, 0); + journal_mark_dirty (th, p_s_sb, p_s_bh); + reiserfs_free_block(th, inode, block, 1); + } - tmp = get_block_num(p_n_unfm_pointer, 0); - put_block_num(p_n_unfm_pointer, 0, 0); - journal_mark_dirty(th, p_s_sb, p_s_bh); - reiserfs_free_block(th, inode, tmp, 1); - if (item_moved(&s_ih, p_s_path)) { - need_research = 1; - break; - } - } + cond_resched(); - /* a trick. If the buffer has been logged, this - ** will do nothing. If we've broken the loop without - ** logging it, it will restore the buffer - ** - */ - reiserfs_restore_prepared_buffer(p_s_sb, p_s_bh); - - /* This loop can be optimized. */ - } while ((*p_n_removed < n_unfm_number || need_research) && - search_for_position_by_key(p_s_sb, p_s_item_key, - p_s_path) == - POSITION_FOUND); - - RFALSE(*p_n_removed < n_unfm_number, - "PAP-5310: indirect item is not found"); - RFALSE(item_moved(&s_ih, p_s_path), - "after while, comp failed, retry"); - - if (c_mode == M_CUT) - pos_in_item(p_s_path) *= UNFM_P_SIZE; - return c_mode; + if (item_moved (&s_ih, p_s_path)) { + need_re_search = 1; + break; + } + + pos --; + (*p_n_removed) ++; + (*p_n_cut_size) -= UNFM_P_SIZE; + + if (pos == 0) { + (*p_n_cut_size) -= IH_SIZE; + result = M_DELETE; + break; + } + } + /* a trick. If the buffer has been logged, this will do nothing. If + ** we've broken the loop without logging it, it will restore the + ** buffer */ + reiserfs_restore_prepared_buffer(p_s_sb, p_s_bh); + } while (need_re_search && + search_for_position_by_key(p_s_sb, p_s_item_key, p_s_path) == POSITION_FOUND); + pos_in_item(p_s_path) = pos * UNFM_P_SIZE; + + if (*p_n_cut_size == 0) { + /* Nothing were cut. maybe convert last unformatted node to the + * direct item? */ + result = M_CONVERT; + } + return result; } } @@ -1948,7 +1881,8 @@ int reiserfs_do_truncate(struct reiserfs_transaction_handle *th, struct inode *p ** sure the file is consistent before ending the current trans ** and starting a new one */ - if (journal_transaction_should_end(th, th->t_blocks_allocated)) { + if (journal_transaction_should_end(th, 0) || + reiserfs_transaction_free_space(th) <= JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD) { int orig_len_alloc = th->t_blocks_allocated; decrement_counters_in_path(&s_search_path); @@ -1962,7 +1896,7 @@ int reiserfs_do_truncate(struct reiserfs_transaction_handle *th, struct inode *p if (err) goto out; err = journal_begin(th, p_s_inode->i_sb, - JOURNAL_PER_BALANCE_CNT * 6); + JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD + JOURNAL_PER_BALANCE_CNT * 4) ; if (err) goto out; reiserfs_update_inode_transaction(p_s_inode); diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index dad78cecfd20..912f1b7cb18f 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h @@ -1704,6 +1704,11 @@ static inline int reiserfs_transaction_running(struct super_block *s) return 0; } +static inline int reiserfs_transaction_free_space(struct reiserfs_transaction_handle *th) +{ + return th->t_blocks_allocated - th->t_blocks_logged; +} + int reiserfs_async_progress_wait(struct super_block *s); struct reiserfs_transaction_handle *reiserfs_persistent_transaction(struct -- cgit v1.2.3 From b500531e6f5f234ed267bd7060ee06d144faf0ca Mon Sep 17 00:00:00 2001 From: Oleg Drokin Date: Sat, 25 Mar 2006 03:07:01 -0800 Subject: [PATCH] Introduce FMODE_EXEC file flag Introduce FMODE_EXEC file flag, to indicate that file is being opened for execution. This is useful for distributed filesystems to maintain consistent behavior for returning ETXTBUSY when opening for write and execution happens on different nodes. akpm: Needed by Lustre at present. I assume their objective to to work towards being able to install Lustre on an unmodified distro kernel, which seems sane. It should have zero runtime cost. Trond and Chuck indicate that NFS4 can probably use this too, for the same thing. Steven says it's also on the GFS todo list. Signed-off-by: Oleg Drokin Cc: Trond Myklebust Cc: Chuck Lever Cc: Steven Whitehouse Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 4 ++-- include/linux/fs.h | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index 0b515ac53134..d8c477a56257 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -127,7 +127,7 @@ asmlinkage long sys_uselib(const char __user * library) struct nameidata nd; int error; - error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ); + error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC); if (error) goto out; @@ -477,7 +477,7 @@ struct file *open_exec(const char *name) int err; struct file *file; - err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ); + err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC); file = ERR_PTR(err); if (!err) { diff --git a/include/linux/fs.h b/include/linux/fs.h index 7c750312261b..21e8cf795c38 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -65,6 +65,11 @@ extern int dir_notify_enable; #define FMODE_PREAD 8 #define FMODE_PWRITE FMODE_PREAD /* These go hand in hand */ +/* File is being opened for execution. Primary users of this flag are + distributed filesystems that can use it to achieve correct ETXTBUSY + behavior for cross-node execution/opening_for_writing of files */ +#define FMODE_EXEC 16 + #define RW_MASK 1 #define RWA_MASK 2 #define READ 0 -- cgit v1.2.3 From 1aef821a6b3aeca8c19d06aee012ed9db617d1e3 Mon Sep 17 00:00:00 2001 From: Thomas Koeller Date: Sat, 25 Mar 2006 03:07:03 -0800 Subject: [PATCH] constify tty flip buffer handling Add a couple of 'const' qualifiers to the TTY flip buffer APIs, where appropriate. Signed-off-by: Thomas Koeller Acked-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 4 ++-- include/linux/tty_flip.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 76592ee1fb38..48d795bb8c4b 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -354,7 +354,7 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size) EXPORT_SYMBOL_GPL(tty_buffer_request_room); -int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, size_t size) +int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, size_t size) { int copied = 0; do { @@ -378,7 +378,7 @@ int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, size_t EXPORT_SYMBOL_GPL(tty_insert_flip_string); -int tty_insert_flip_string_flags(struct tty_struct *tty, unsigned char *chars, char *flags, size_t size) +int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size) { int copied = 0; do { diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 0c6169fff366..0976a163b459 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -2,8 +2,8 @@ #define _LINUX_TTY_FLIP_H extern int tty_buffer_request_room(struct tty_struct *tty, size_t size); -extern int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, size_t size); -extern int tty_insert_flip_string_flags(struct tty_struct *tty, unsigned char *chars, char *flags, size_t size); +extern int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, size_t size); +extern int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size); extern int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, size_t size); extern int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size); -- cgit v1.2.3 From 8d3b33f67fdc0fb364a1ef6d8fbbea7c2e4e6c98 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 25 Mar 2006 03:07:05 -0800 Subject: [PATCH] Remove MODULE_PARM MODULE_PARM was actually breaking: recent gcc version optimize them out as unused. It's time to replace the last users, which are generally in the most unloved drivers anyway. Signed-off-by: Rusty Russell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/networking/ray_cs.txt | 2 +- Documentation/sound/oss/Introduction | 2 +- Documentation/sound/oss/cs46xx | 16 +-- arch/ppc/8xx_io/cs4218_tdm.c | 10 +- drivers/block/ataflop.c | 4 +- drivers/cdrom/cm206.c | 8 +- drivers/cdrom/sbpcd.c | 10 +- drivers/char/istallion.c | 8 +- drivers/char/mxser.c | 8 +- drivers/char/riscom8.c | 8 +- drivers/isdn/hardware/avm/b1dma.c | 2 +- drivers/isdn/hardware/avm/b1isa.c | 4 +- drivers/isdn/hardware/avm/c4.c | 2 +- drivers/isdn/hardware/avm/t1isa.c | 6 +- drivers/isdn/hysdn/hycapi.c | 2 +- drivers/isdn/hysdn/hysdn_net.c | 2 +- drivers/isdn/isdnloop/isdnloop.c | 2 +- drivers/media/video/zr36120.c | 8 +- drivers/mmc/au1xmmc.c | 2 +- drivers/mtd/maps/pcmciamtd.c | 14 +-- drivers/net/atari_bionet.c | 2 +- drivers/net/atari_pamsnet.c | 2 +- drivers/net/atarilance.c | 2 +- drivers/net/cassini.c | 11 ++- drivers/net/chelsio/cxgb2.c | 2 +- drivers/net/fec_8xx/fec_main.c | 4 +- drivers/net/fs_enet/fs_enet-main.c | 4 +- drivers/net/gt96100eth.c | 4 +- drivers/net/hamradio/dmascc.c | 2 +- drivers/net/hamradio/mkiss.c | 2 +- drivers/net/irda/irport.c | 4 +- drivers/net/lasi_82596.c | 6 +- drivers/net/mac89x0.c | 2 +- drivers/net/mace.c | 2 +- drivers/net/meth.c | 2 +- drivers/net/ne-h8300.c | 6 +- drivers/net/ni5010.c | 4 +- drivers/net/sun3lance.c | 2 +- drivers/s390/block/dasd.c | 1 - drivers/s390/block/dasd_devmap.c | 3 + drivers/scsi/arm/cumana_2.c | 2 +- drivers/scsi/arm/eesox.c | 2 +- drivers/scsi/arm/powertec.c | 2 +- drivers/scsi/atari_scsi.c | 10 +- drivers/video/pm3fb.c | 18 ++-- include/linux/module.h | 19 ---- include/video/pm3fb.h | 3 - init/Kconfig | 9 -- kernel/module.c | 183 +++-------------------------------- kernel/rcutorture.c | 10 +- sound/oss/au1000.c | 2 +- sound/oss/au1550_ac97.c | 2 +- sound/oss/dmasound/dmasound_core.c | 10 +- sound/oss/ite8172.c | 4 +- sound/oss/swarm_cs4297a.c | 4 +- sound/oss/waveartist.c | 8 +- 56 files changed, 146 insertions(+), 329 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ray_cs.txt b/Documentation/networking/ray_cs.txt index 5427f8c7df95..145d27a52395 100644 --- a/Documentation/networking/ray_cs.txt +++ b/Documentation/networking/ray_cs.txt @@ -25,7 +25,7 @@ the essid= string parameter is available via the kernel command line. This will change after the method of sorting out parameters for all the PCMCIA drivers is agreed upon. If you must have a built in driver with nondefault parameters, they can be edited in -/usr/src/linux/drivers/net/pcmcia/ray_cs.c. Searching for MODULE_PARM +/usr/src/linux/drivers/net/pcmcia/ray_cs.c. Searching for module_param will find them all. Information on card services is available at: diff --git a/Documentation/sound/oss/Introduction b/Documentation/sound/oss/Introduction index 15d4fb975ac0..f04ba6bb7395 100644 --- a/Documentation/sound/oss/Introduction +++ b/Documentation/sound/oss/Introduction @@ -69,7 +69,7 @@ are available, for example IRQ, address, DMA. Warning, the options for different cards sometime use different names for the same or a similar feature (dma1= versus dma16=). As a last -resort, inspect the code (search for MODULE_PARM). +resort, inspect the code (search for module_param). Notes: diff --git a/Documentation/sound/oss/cs46xx b/Documentation/sound/oss/cs46xx index 88d6cf8b39f3..b54432709863 100644 --- a/Documentation/sound/oss/cs46xx +++ b/Documentation/sound/oss/cs46xx @@ -88,7 +88,7 @@ parameters. for a copy email: twoller@crystal.cirrus.com MODULE_PARMS definitions ------------------------ -MODULE_PARM(defaultorder, "i"); +module_param(defaultorder, ulong, 0); defaultorder=N where N is a value from 1 to 12 The buffer order determines the size of the dma buffer for the driver. @@ -98,18 +98,18 @@ to not underrun the dma buffer as easily. As default, use 32k (order=3) rather than 64k as some of the games work more responsively. (2^N) * PAGE_SIZE = allocated buffer size -MODULE_PARM(cs_debuglevel, "i"); -MODULE_PARM(cs_debugmask, "i"); +module_param(cs_debuglevel, ulong, 0644); +module_param(cs_debugmask, ulong, 0644); cs_debuglevel=N cs_debugmask=0xMMMMMMMM where N is a value from 0 (no debug printfs), to 9 (maximum) 0xMMMMMMMM is a debug mask corresponding to the CS_xxx bits (see driver source). -MODULE_PARM(hercules_egpio_disable, "i"); +module_param(hercules_egpio_disable, ulong, 0); hercules_egpio_disable=N where N is a 0 (enable egpio), or a 1 (disable egpio support) -MODULE_PARM(initdelay, "i"); +module_param(initdelay, ulong, 0); initdelay=N This value is used to determine the millescond delay during the initialization code prior to powering up the PLL. On laptops this value can be used to @@ -118,19 +118,19 @@ system is booted under battery power then the mdelay()/udelay() functions fail t properly delay the required time. Also, if the system is booted under AC power and then the power removed, the mdelay()/udelay() functions will not delay properly. -MODULE_PARM(powerdown, "i"); +module_param(powerdown, ulong, 0); powerdown=N where N is 0 (disable any powerdown of the internal blocks) or 1 (enable powerdown) -MODULE_PARM(external_amp, "i"); +module_param(external_amp, bool, 0); external_amp=1 if N is set to 1, then force enabling the EAPD support in the primary AC97 codec. override the detection logic and force the external amp bit in the AC97 0x26 register to be reset (0). EAPD should be 0 for powerup, and 1 for powerdown. The VTB Santa Cruz card has inverted logic, so there is a special function for these cards. -MODULE_PARM(thinkpad, "i"); +module_param(thinkpad, bool, 0); thinkpad=1 if N is set to 1, then force enabling the clkrun functionality. Currently, when the part is being used, then clkrun is disabled for the entire system, diff --git a/arch/ppc/8xx_io/cs4218_tdm.c b/arch/ppc/8xx_io/cs4218_tdm.c index 49eb2a7e65c0..a892356d5c3b 100644 --- a/arch/ppc/8xx_io/cs4218_tdm.c +++ b/arch/ppc/8xx_io/cs4218_tdm.c @@ -126,11 +126,11 @@ static int numReadBufs = 4, readbufSize = 32; */ static volatile cbd_t *rx_base, *rx_cur, *tx_base, *tx_cur; -MODULE_PARM(catchRadius, "i"); -MODULE_PARM(numBufs, "i"); -MODULE_PARM(bufSize, "i"); -MODULE_PARM(numreadBufs, "i"); -MODULE_PARM(readbufSize, "i"); +module_param(catchRadius, int, 0); +module_param(numBufs, int, 0); +module_param(bufSize, int, 0); +module_param(numreadBufs, int, 0); +module_param(readbufSize, int, 0); #define arraysize(x) (sizeof(x)/sizeof(*(x))) #define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index f8ce235ccfc3..c39650920bdf 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -271,7 +271,7 @@ unsigned char *DMABuffer; /* buffer for writes */ static unsigned long PhysDMABuffer; /* physical address */ static int UseTrackbuffer = -1; /* Do track buffering? */ -MODULE_PARM(UseTrackbuffer, "i"); +module_param(UseTrackbuffer, int, 0); unsigned char *TrackBuffer; /* buffer for reads */ static unsigned long PhysTrackBuffer; /* physical address */ @@ -296,7 +296,7 @@ static int MotorOn = 0, MotorOffTrys; static int IsFormatting = 0, FormatError; static int UserSteprate[FD_MAX_UNITS] = { -1, -1 }; -MODULE_PARM(UserSteprate, "1-" __MODULE_STRING(FD_MAX_UNITS) "i"); +module_param_array(UserSteprate, int, NULL, 0); /* Synchronization of FDC access. */ static volatile int fdc_busy = 0; diff --git a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c index fad27a87ce35..f43a988dd413 100644 --- a/drivers/cdrom/cm206.c +++ b/drivers/cdrom/cm206.c @@ -218,12 +218,12 @@ static int cm206_base = CM206_BASE; static int cm206_irq = CM206_IRQ; #ifdef MODULE static int cm206[2] = { 0, 0 }; /* for compatible `insmod' parameter passing */ +module_param_array(cm206, int, NULL, 0); /* base,irq or irq,base */ #endif -MODULE_PARM(cm206_base, "i"); /* base */ -MODULE_PARM(cm206_irq, "i"); /* irq */ -MODULE_PARM(cm206, "1-2i"); /* base,irq or irq,base */ -MODULE_PARM(auto_probe, "i"); /* auto probe base and irq */ +module_param(cm206_base, int, 0); /* base */ +module_param(cm206_irq, int, 0); /* irq */ +module_param(auto_probe, bool, 0); /* auto probe base and irq */ MODULE_LICENSE("GPL"); #define POLLOOP 100 /* milliseconds */ diff --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c index 4760f515f591..05c9e865ecaf 100644 --- a/drivers/cdrom/sbpcd.c +++ b/drivers/cdrom/sbpcd.c @@ -464,8 +464,13 @@ static int sbpcd[] = static __cacheline_aligned DEFINE_SPINLOCK(sbpcd_lock); static struct request_queue *sbpcd_queue; -MODULE_PARM(sbpcd, "2i"); -MODULE_PARM(max_drives, "i"); +/* You can only set the first pair, from old MODULE_PARM code. */ +static int sbpcd_set(const char *val, struct kernel_param *kp) +{ + get_options((char *)val, 2, (int *)sbpcd); + return 0; +} +module_param_call(sbpcd, sbpcd_set, NULL, NULL, 0); #define NUM_PROBE (sizeof(sbpcd) / sizeof(int)) @@ -553,6 +558,7 @@ static unsigned char msgnum; static char msgbuf[80]; static int max_drives = MAX_DRIVES; +module_param(max_drives, int, 0); #ifndef MODULE static unsigned char setup_done; static const char *str_sb_l = "soundblaster"; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index ede128356af2..e5247f85a446 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -378,13 +378,13 @@ MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver"); MODULE_LICENSE("GPL"); -MODULE_PARM(board0, "1-3s"); +module_param_array(board0, charp, NULL, 0); MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]"); -MODULE_PARM(board1, "1-3s"); +module_param_array(board1, charp, NULL, 0); MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,memaddr]"); -MODULE_PARM(board2, "1-3s"); +module_param_array(board2, charp, NULL, 0); MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,memaddr]"); -MODULE_PARM(board3, "1-3s"); +module_param_array(board3, charp, NULL, 0); MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,memaddr]"); #endif diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index ea725a9964e2..0fb2fb9fb024 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -243,10 +243,10 @@ static int verbose = 0; MODULE_AUTHOR("Casper Yang"); MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); -MODULE_PARM(ioaddr, "1-4i"); -MODULE_PARM(ttymajor, "i"); -MODULE_PARM(calloutmajor, "i"); -MODULE_PARM(verbose, "i"); +module_param_array(ioaddr, int, NULL, 0); +module_param(ttymajor, int, 0); +module_param(calloutmajor, int, 0); +module_param(verbose, bool, 0); MODULE_LICENSE("GPL"); struct mxser_log { diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 119e629656b7..657c0d88f48c 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1743,10 +1743,10 @@ static int iobase; static int iobase1; static int iobase2; static int iobase3; -MODULE_PARM(iobase, "i"); -MODULE_PARM(iobase1, "i"); -MODULE_PARM(iobase2, "i"); -MODULE_PARM(iobase3, "i"); +module_param(iobase, int, 0); +module_param(iobase1, int, 0); +module_param(iobase2, int, 0); +module_param(iobase3, int, 0); MODULE_LICENSE("GPL"); #endif /* MODULE */ diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c index 91dd0551fc7c..4d64e5cbcdbf 100644 --- a/drivers/isdn/hardware/avm/b1dma.c +++ b/drivers/isdn/hardware/avm/b1dma.c @@ -39,7 +39,7 @@ MODULE_AUTHOR("Carsten Paeth"); MODULE_LICENSE("GPL"); static int suppress_pollack = 0; -MODULE_PARM(suppress_pollack, "0-1i"); +module_param(suppress_pollack, bool, 0); /* ------------------------------------------------------------- */ diff --git a/drivers/isdn/hardware/avm/b1isa.c b/drivers/isdn/hardware/avm/b1isa.c index 38bd4dfecbd1..80fb488848b8 100644 --- a/drivers/isdn/hardware/avm/b1isa.c +++ b/drivers/isdn/hardware/avm/b1isa.c @@ -169,8 +169,8 @@ static struct pci_dev isa_dev[MAX_CARDS]; static int io[MAX_CARDS]; static int irq[MAX_CARDS]; -MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i"); -MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i"); +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); MODULE_PARM_DESC(io, "I/O base address(es)"); MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c index 724aac2c1cca..f7253b2136ea 100644 --- a/drivers/isdn/hardware/avm/c4.c +++ b/drivers/isdn/hardware/avm/c4.c @@ -50,7 +50,7 @@ MODULE_DEVICE_TABLE(pci, c4_pci_tbl); MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM C2/C4 cards"); MODULE_AUTHOR("Carsten Paeth"); MODULE_LICENSE("GPL"); -MODULE_PARM(suppress_pollack, "0-1i"); +module_param(suppress_pollack, bool, 0); /* ------------------------------------------------------------- */ diff --git a/drivers/isdn/hardware/avm/t1isa.c b/drivers/isdn/hardware/avm/t1isa.c index 3b701d97bdf1..5a2f854d55b5 100644 --- a/drivers/isdn/hardware/avm/t1isa.c +++ b/drivers/isdn/hardware/avm/t1isa.c @@ -519,9 +519,9 @@ static int io[MAX_CARDS]; static int irq[MAX_CARDS]; static int cardnr[MAX_CARDS]; -MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i"); -MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i"); -MODULE_PARM(cardnr, "1-" __MODULE_STRING(MAX_CARDS) "i"); +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +module_param_array(cardnr, int, NULL, 0); MODULE_PARM_DESC(io, "I/O base address(es)"); MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)"); diff --git a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c index 55fbea0b6ac8..6bac43cc91bd 100644 --- a/drivers/isdn/hysdn/hycapi.c +++ b/drivers/isdn/hysdn/hycapi.c @@ -31,7 +31,7 @@ static char hycapi_revision[]="$Revision: 1.8.6.4 $"; unsigned int hycapi_enable = 0xffffffff; -MODULE_PARM(hycapi_enable, "i"); +module_param(hycapi_enable, uint, 0); typedef struct _hycapi_appl { unsigned int ctrl_mask; diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c index 683647b1f2bc..d205249a1242 100644 --- a/drivers/isdn/hysdn/hysdn_net.c +++ b/drivers/isdn/hysdn/hysdn_net.c @@ -24,7 +24,7 @@ #include "hysdn_defs.h" unsigned int hynet_enable = 0xffffffff; -MODULE_PARM(hynet_enable, "i"); +module_param(hynet_enable, uint, 0); /* store the actual version for log reporting */ char *hysdn_net_revision = "$Revision: 1.8.6.4 $"; diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c index 33d339700411..a67d31af797a 100644 --- a/drivers/isdn/isdnloop/isdnloop.c +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -22,7 +22,7 @@ static char *isdnloop_id = "loop0"; MODULE_DESCRIPTION("ISDN4Linux: Pseudo Driver that simulates an ISDN card"); MODULE_AUTHOR("Fritz Elfert"); MODULE_LICENSE("GPL"); -MODULE_PARM(isdnloop_id, "s"); +module_param(isdnloop_id, charp, 0); MODULE_PARM_DESC(isdnloop_id, "ID-String of first card"); static int isdnloop_addcard(char *); diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c index d4c633b8a7f5..417a999afee3 100644 --- a/drivers/media/video/zr36120.c +++ b/drivers/media/video/zr36120.c @@ -70,10 +70,10 @@ MODULE_AUTHOR("Pauline Middelink "); MODULE_DESCRIPTION("Zoran ZR36120 based framegrabber"); MODULE_LICENSE("GPL"); -MODULE_PARM(triton1,"i"); -MODULE_PARM(cardtype,"1-" __MODULE_STRING(ZORAN_MAX) "i"); -MODULE_PARM(video_nr,"i"); -MODULE_PARM(vbi_nr,"i"); +module_param(triton1, uint, 0); +module_param_array(cardtype, uint, NULL, 0); +module_param(video_nr, int, 0); +module_param(vbi_nr, int, 0); static int zoran_cards; static struct zoran zorans[ZORAN_MAX]; diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 8d84b045bc83..85e89c77bdea 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -87,7 +87,7 @@ struct au1xmmc_host *au1xmmc_hosts[AU1XMMC_CONTROLLER_COUNT]; static int dma = 1; #ifdef MODULE -MODULE_PARM(dma, "i"); +module_param(dma, bool, 0); MODULE_PARM_DESC(dma, "Use DMA engine for data transfers (0 = disabled)"); #endif diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index f0f8916da7ad..f988c817e196 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -28,7 +28,7 @@ #ifdef CONFIG_MTD_DEBUG static int debug = CONFIG_MTD_DEBUG_VERBOSE; -MODULE_PARM(debug, "i"); +module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); #undef DEBUG #define DEBUG(n, format, arg...) \ @@ -89,17 +89,17 @@ static int mem_type; MODULE_LICENSE("GPL"); MODULE_AUTHOR("Simon Evans "); MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_PARM(bankwidth, "i"); +module_param(bankwidth, int, 0); MODULE_PARM_DESC(bankwidth, "Set bankwidth (1=8 bit, 2=16 bit, default=2)"); -MODULE_PARM(mem_speed, "i"); +module_param(mem_speed, int, 0); MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns"); -MODULE_PARM(force_size, "i"); +module_param(force_size, int, 0); MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)"); -MODULE_PARM(setvpp, "i"); +module_param(setvpp, int, 0); MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)"); -MODULE_PARM(vpp, "i"); +module_param(vpp, int, 0); MODULE_PARM_DESC(vpp, "Vpp value in 1/10ths eg 33=3.3V 120=12V (Dangerous)"); -MODULE_PARM(mem_type, "i"); +module_param(mem_type, int, 0); MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)"); diff --git a/drivers/net/atari_bionet.c b/drivers/net/atari_bionet.c index 0095384ff454..5e5f80b99b9e 100644 --- a/drivers/net/atari_bionet.c +++ b/drivers/net/atari_bionet.c @@ -123,7 +123,7 @@ static char version[] = * Global variable 'bionet_debug'. Can be set at load time by 'insmod' */ unsigned int bionet_debug = NET_DEBUG; -MODULE_PARM(bionet_debug, "i"); +module_param(bionet_debug, int, 0); MODULE_PARM_DESC(bionet_debug, "bionet debug level (0-2)"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/atari_pamsnet.c b/drivers/net/atari_pamsnet.c index 8b997809f9de..d6039e62d832 100644 --- a/drivers/net/atari_pamsnet.c +++ b/drivers/net/atari_pamsnet.c @@ -119,7 +119,7 @@ static char *version = * Global variable 'pamsnet_debug'. Can be set at load time by 'insmod' */ unsigned int pamsnet_debug = NET_DEBUG; -MODULE_PARM(pamsnet_debug, "i"); +module_param(pamsnet_debug, int, 0); MODULE_PARM_DESC(pamsnet_debug, "pamsnet debug enable (0-1)"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c index e01b6a78ec63..442b2cbeb58a 100644 --- a/drivers/net/atarilance.c +++ b/drivers/net/atarilance.c @@ -78,7 +78,7 @@ static int lance_debug = LANCE_DEBUG; #else static int lance_debug = 1; #endif -MODULE_PARM(lance_debug, "i"); +module_param(lance_debug, int, 0); MODULE_PARM_DESC(lance_debug, "atarilance debug level (0-3)"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 8f1573e658a5..ac48f7543500 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -192,12 +192,15 @@ static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; +static int cassini_debug = -1; /* -1 == use CAS_DEF_MSG_ENABLE as value */ +static int link_mode; + MODULE_AUTHOR("Adrian Sun (asun@darksunrising.com)"); MODULE_DESCRIPTION("Sun Cassini(+) ethernet driver"); MODULE_LICENSE("GPL"); -MODULE_PARM(cassini_debug, "i"); +module_param(cassini_debug, int, 0); MODULE_PARM_DESC(cassini_debug, "Cassini bitmapped debugging message enable value"); -MODULE_PARM(link_mode, "i"); +module_param(link_mode, int, 0); MODULE_PARM_DESC(link_mode, "default link mode"); /* @@ -209,7 +212,7 @@ MODULE_PARM_DESC(link_mode, "default link mode"); * Value in seconds, for user input. */ static int linkdown_timeout = DEFAULT_LINKDOWN_TIMEOUT; -MODULE_PARM(linkdown_timeout, "i"); +module_param(linkdown_timeout, int, 0); MODULE_PARM_DESC(linkdown_timeout, "min reset interval in sec. for PCS linkdown issue; disabled if not positive"); @@ -221,8 +224,6 @@ MODULE_PARM_DESC(linkdown_timeout, static int link_transition_timeout; -static int cassini_debug = -1; /* -1 == use CAS_DEF_MSG_ENABLE as value */ -static int link_mode; static u16 link_modes[] __devinitdata = { BMCR_ANENABLE, /* 0 : autoneg */ diff --git a/drivers/net/chelsio/cxgb2.c b/drivers/net/chelsio/cxgb2.c index 349ebe783ed6..7fe2638ae06d 100644 --- a/drivers/net/chelsio/cxgb2.c +++ b/drivers/net/chelsio/cxgb2.c @@ -124,7 +124,7 @@ MODULE_LICENSE("GPL"); static int dflt_msg_enable = DFLT_MSG_ENABLE; -MODULE_PARM(dflt_msg_enable, "i"); +module_param(dflt_msg_enable, int, 0); MODULE_PARM_DESC(dflt_msg_enable, "Chelsio T1 message enable bitmap"); diff --git a/drivers/net/fec_8xx/fec_main.c b/drivers/net/fec_8xx/fec_main.c index b4f3a9f8a535..7e4338097139 100644 --- a/drivers/net/fec_8xx/fec_main.c +++ b/drivers/net/fec_8xx/fec_main.c @@ -55,11 +55,11 @@ MODULE_AUTHOR("Pantelis Antoniou "); MODULE_DESCRIPTION("Motorola 8xx FEC ethernet driver"); MODULE_LICENSE("GPL"); -MODULE_PARM(fec_8xx_debug, "i"); +int fec_8xx_debug = -1; /* -1 == use FEC_8XX_DEF_MSG_ENABLE as value */ +module_param(fec_8xx_debug, int, 0); MODULE_PARM_DESC(fec_8xx_debug, "FEC 8xx bitmapped debugging message enable value"); -int fec_8xx_debug = -1; /* -1 == use FEC_8XX_DEF_MSG_ENABLE as value */ /*************************************************/ diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index f5d49a110654..196298f33db8 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -58,11 +58,11 @@ MODULE_DESCRIPTION("Freescale Ethernet Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); -MODULE_PARM(fs_enet_debug, "i"); +int fs_enet_debug = -1; /* -1 == use FS_ENET_DEF_MSG_ENABLE as value */ +module_param(fs_enet_debug, int, 0); MODULE_PARM_DESC(fs_enet_debug, "Freescale bitmapped debugging message enable value"); -int fs_enet_debug = -1; /* -1 == use FS_ENET_DEF_MSG_ENABLE as value */ static void fs_set_multicast_list(struct net_device *dev) { diff --git a/drivers/net/gt96100eth.c b/drivers/net/gt96100eth.c index 5958a6314723..2d2435404614 100644 --- a/drivers/net/gt96100eth.c +++ b/drivers/net/gt96100eth.c @@ -114,8 +114,8 @@ static int max_interrupt_work = 32; static char mac0[18] = "00.02.03.04.05.06"; static char mac1[18] = "00.01.02.03.04.05"; -MODULE_PARM(mac0, "c18"); -MODULE_PARM(mac1, "c18"); +module_param_string(mac0, mac0, 18, 0); +module_param_string(mac1, mac0, 18, 0); MODULE_PARM_DESC(mac0, "MAC address for GT96100 ethernet port 0"); MODULE_PARM_DESC(mac1, "MAC address for GT96100 ethernet port 1"); diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index c8dc40214a08..79a8fbcf5f93 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -280,7 +280,7 @@ static unsigned long rand; MODULE_AUTHOR("Klaus Kudielka"); MODULE_DESCRIPTION("Driver for high-speed SCC boards"); -MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i"); +module_param_array(io, int, NULL, 0); MODULE_LICENSE("GPL"); static void __exit dmascc_exit(void) diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index dc5e9d59deed..d81a8e1eeb8d 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -1012,7 +1012,7 @@ static void __exit mkiss_exit_driver(void) MODULE_AUTHOR("Ralf Baechle DL5RB "); MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs"); -MODULE_PARM(crc_force, "i"); +module_param(crc_force, int, 0); MODULE_PARM_DESC(crc_force, "crc [0 = auto | 1 = none | 2 = flexnet | 3 = smack]"); MODULE_LICENSE("GPL"); MODULE_ALIAS_LDISC(N_AX25); diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c index 6070195b87bd..98fa5319e5cc 100644 --- a/drivers/net/irda/irport.c +++ b/drivers/net/irda/irport.c @@ -1118,9 +1118,9 @@ static void __exit irport_cleanup(void) } } -MODULE_PARM(io, "1-4i"); +module_param_array(io, int, NULL, 0); MODULE_PARM_DESC(io, "Base I/O addresses"); -MODULE_PARM(irq, "1-4i"); +module_param_array(irq, int, NULL, 0); MODULE_PARM_DESC(irq, "IRQ lines"); MODULE_AUTHOR("Dag Brattli "); diff --git a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c index f7b7238d8352..957888de3d7e 100644 --- a/drivers/net/lasi_82596.c +++ b/drivers/net/lasi_82596.c @@ -177,7 +177,7 @@ static int i596_debug = (DEB_SERIOUS|DEB_PROBE); MODULE_AUTHOR("Richard Hirst"); MODULE_DESCRIPTION("i82596 driver"); MODULE_LICENSE("GPL"); -MODULE_PARM(i596_debug, "i"); +module_param(i596_debug, int, 0); MODULE_PARM_DESC(i596_debug, "lasi_82596 debug mask"); /* Copy frames shorter than rx_copybreak, otherwise pass on up in @@ -1520,9 +1520,9 @@ static void set_multicast_list(struct net_device *dev) } } -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "lasi_82596 debug mask"); static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "lasi_82596 debug mask"); static int num_drivers; static struct net_device *netdevs[MAX_DRIVERS]; diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c index f65b0db111b8..cd3c9a5a98b2 100644 --- a/drivers/net/mac89x0.c +++ b/drivers/net/mac89x0.c @@ -629,7 +629,7 @@ static int set_mac_address(struct net_device *dev, void *addr) static struct net_device *dev_cs89x0; static int debug; -MODULE_PARM(debug, "i"); +module_param(debug, int, 0); MODULE_PARM_DESC(debug, "CS89[02]0 debug level (0-5)"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/mace.c b/drivers/net/mace.c index 2a5add257b8f..77792b286027 100644 --- a/drivers/net/mace.c +++ b/drivers/net/mace.c @@ -1042,7 +1042,7 @@ static void __exit mace_cleanup(void) MODULE_AUTHOR("Paul Mackerras"); MODULE_DESCRIPTION("PowerMac MACE driver."); -MODULE_PARM(port_aaui, "i"); +module_param(port_aaui, int, 0); MODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/meth.c b/drivers/net/meth.c index e23655f5049f..d644bf3a933c 100644 --- a/drivers/net/meth.c +++ b/drivers/net/meth.c @@ -62,7 +62,7 @@ MODULE_DESCRIPTION("SGI O2 Builtin Fast Ethernet driver"); #ifdef HAVE_TX_TIMEOUT static int timeout = TX_TIMEOUT; -MODULE_PARM(timeout, "i"); +module_param(timeout, int, 0); #endif /* diff --git a/drivers/net/ne-h8300.c b/drivers/net/ne-h8300.c index aaebd28a1920..7ea3d596ac3b 100644 --- a/drivers/net/ne-h8300.c +++ b/drivers/net/ne-h8300.c @@ -601,9 +601,9 @@ static int io[MAX_NE_CARDS]; static int irq[MAX_NE_CARDS]; static int bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */ -MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); -MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); -MODULE_PARM(bad, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +module_param_array(bad, int, NULL, 0); MODULE_PARM_DESC(io, "I/O base address(es)"); MODULE_PARM_DESC(irq, "IRQ number(s)"); MODULE_DESCRIPTION("H8/300 NE2000 Ethernet driver"); diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c index 2ab01a5d1d22..a68bf474f6ed 100644 --- a/drivers/net/ni5010.c +++ b/drivers/net/ni5010.c @@ -766,8 +766,8 @@ static void ni5010_show_registers(struct net_device *dev) #ifdef MODULE static struct net_device *dev_ni5010; -MODULE_PARM(io, "i"); -MODULE_PARM(irq, "i"); +module_param(io, int, 0); +module_param(irq, int, 0); MODULE_PARM_DESC(io, "ni5010 I/O base address"); MODULE_PARM_DESC(irq, "ni5010 IRQ number"); diff --git a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c index 01bdb2334058..d4c0002b43db 100644 --- a/drivers/net/sun3lance.c +++ b/drivers/net/sun3lance.c @@ -71,7 +71,7 @@ static int lance_debug = LANCE_DEBUG; #else static int lance_debug = 1; #endif -MODULE_PARM(lance_debug, "i"); +module_param(lance_debug, int, 0); MODULE_PARM_DESC(lance_debug, "SUN3 Lance debug level (0-3)"); MODULE_LICENSE("GPL"); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index dfe542b206cc..7967916bda18 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -43,7 +43,6 @@ MODULE_AUTHOR("Holger Smolinski "); MODULE_DESCRIPTION("Linux on S/390 DASD device driver," " Copyright 2000 IBM Corporation"); MODULE_SUPPORTED_DEVICE("dasd"); -MODULE_PARM(dasd, "1-" __MODULE_STRING(256) "s"); MODULE_LICENSE("GPL"); /* diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index f576f243cd93..2f720108a7e0 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -69,6 +70,8 @@ int dasd_autodetect = 0; /* is true, when autodetection is active */ * strings when running as a module. */ static char *dasd[256]; +module_param_array(dasd, charp, NULL, 0); + /* * Single spinlock to protect devmap structures and lists. */ diff --git a/drivers/scsi/arm/cumana_2.c b/drivers/scsi/arm/cumana_2.c index 583d2d8c8335..fad2109268bb 100644 --- a/drivers/scsi/arm/cumana_2.c +++ b/drivers/scsi/arm/cumana_2.c @@ -550,6 +550,6 @@ module_exit(cumanascsi2_exit); MODULE_AUTHOR("Russell King"); MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines"); -MODULE_PARM(term, "1-8i"); +module_param_array(term, int, NULL, 0); MODULE_PARM_DESC(term, "SCSI bus termination"); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/arm/eesox.c b/drivers/scsi/arm/eesox.c index 3ffec7efc9d5..dcbb4b2b3fe0 100644 --- a/drivers/scsi/arm/eesox.c +++ b/drivers/scsi/arm/eesox.c @@ -674,6 +674,6 @@ module_exit(eesox_exit); MODULE_AUTHOR("Russell King"); MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines"); -MODULE_PARM(term, "1-8i"); +module_param_array(term, int, NULL, 0); MODULE_PARM_DESC(term, "SCSI bus termination"); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/arm/powertec.c b/drivers/scsi/arm/powertec.c index 3113bdcedb13..3d69f6c45a6b 100644 --- a/drivers/scsi/arm/powertec.c +++ b/drivers/scsi/arm/powertec.c @@ -466,6 +466,6 @@ module_exit(powertecscsi_exit); MODULE_AUTHOR("Russell King"); MODULE_DESCRIPTION("Powertec SCSI driver"); -MODULE_PARM(term, "1-8i"); +module_param_array(term, int, NULL, 0); MODULE_PARM_DESC(term, "SCSI bus termination"); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c index f4c1ca7c1572..f677c5a32a68 100644 --- a/drivers/scsi/atari_scsi.c +++ b/drivers/scsi/atari_scsi.c @@ -239,17 +239,17 @@ static int atari_read_overruns = 0; #endif static int setup_can_queue = -1; -MODULE_PARM(setup_can_queue, "i"); +module_param(setup_can_queue, int, 0); static int setup_cmd_per_lun = -1; -MODULE_PARM(setup_cmd_per_lun, "i"); +module_param(setup_cmd_per_lun, int, 0); static int setup_sg_tablesize = -1; -MODULE_PARM(setup_sg_tablesize, "i"); +module_param(setup_sg_tablesize, int, 0); #ifdef SUPPORT_TAGS static int setup_use_tagged_queuing = -1; -MODULE_PARM(setup_use_tagged_queuing, "i"); +module_param(setup_use_tagged_queuing, int, 0); #endif static int setup_hostid = -1; -MODULE_PARM(setup_hostid, "i"); +module_param(setup_hostid, int, 0); #if defined(CONFIG_TT_DMA_EMUL) diff --git a/drivers/video/pm3fb.c b/drivers/video/pm3fb.c index 0e78ddc81583..52c18a35fb41 100644 --- a/drivers/video/pm3fb.c +++ b/drivers/video/pm3fb.c @@ -3532,26 +3532,26 @@ int __init pm3fb_init(void) MODULE_AUTHOR("Romain Dolbeau"); MODULE_DESCRIPTION("Permedia3 framebuffer device driver"); static char *mode[PM3_MAX_BOARD]; -MODULE_PARM(mode,PM3_MAX_BOARD_MODULE_ARRAY_STRING); +module_param_array(mode, charp, NULL, 0); MODULE_PARM_DESC(mode,"video mode"); -MODULE_PARM(disable,PM3_MAX_BOARD_MODULE_ARRAY_SHORT); +module_param_array(disable, short, NULL, 0); MODULE_PARM_DESC(disable,"disable board"); static short off[PM3_MAX_BOARD]; -MODULE_PARM(off,PM3_MAX_BOARD_MODULE_ARRAY_SHORT); +module_param_array(off, short, NULL, 0); MODULE_PARM_DESC(off,"disable board"); static char *pciid[PM3_MAX_BOARD]; -MODULE_PARM(pciid,PM3_MAX_BOARD_MODULE_ARRAY_STRING); +module_param_array(pciid, charp, NULL, 0); MODULE_PARM_DESC(pciid,"board PCI Id"); -MODULE_PARM(noaccel,PM3_MAX_BOARD_MODULE_ARRAY_SHORT); +module_param_array(noaccel, short, NULL, 0); MODULE_PARM_DESC(noaccel,"disable accel"); static char *font[PM3_MAX_BOARD]; -MODULE_PARM(font,PM3_MAX_BOARD_MODULE_ARRAY_STRING); +module_param_array(font, charp, NULL, 0); MODULE_PARM_DESC(font,"choose font"); -MODULE_PARM(depth,PM3_MAX_BOARD_MODULE_ARRAY_SHORT); +module_param(depth, short, NULL, 0); MODULE_PARM_DESC(depth,"boot-time depth"); -MODULE_PARM(printtimings, "h"); +module_param(printtimings, short, NULL, 0); MODULE_PARM_DESC(printtimings, "print the memory timings of the card(s)"); -MODULE_PARM(forcesize, PM3_MAX_BOARD_MODULE_ARRAY_SHORT); +module_param(forcesize, short, NULL, 0); MODULE_PARM_DESC(forcesize, "force specified memory size"); /* MODULE_SUPPORTED_DEVICE("Permedia3 PCI boards") diff --git a/include/linux/module.h b/include/linux/module.h index 70bd843c71cb..e144309836af 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -554,25 +554,6 @@ static inline void module_remove_driver(struct device_driver *driver) /* BELOW HERE ALL THESE ARE OBSOLETE AND WILL VANISH */ -struct obsolete_modparm { - char name[64]; - char type[64-sizeof(void *)]; - void *addr; -}; - -static inline void MODULE_PARM_(void) { } -#ifdef MODULE -/* DEPRECATED: Do not use. */ -#define MODULE_PARM(var,type) \ -extern struct obsolete_modparm __parm_##var \ -__attribute__((section("__obsparm"))); \ -struct obsolete_modparm __parm_##var = \ -{ __stringify(var), type, &MODULE_PARM_ }; \ -__MODULE_PARM_TYPE(var, type); -#else -#define MODULE_PARM(var,type) static void __attribute__((__unused__)) *__parm_##var = &MODULE_PARM_; -#endif - #define __MODULE_STRING(x) __stringify(x) /* Use symbol_get and symbol_put instead. You'll thank me. */ diff --git a/include/video/pm3fb.h b/include/video/pm3fb.h index 6f4ea808cf74..ac021379ac40 100644 --- a/include/video/pm3fb.h +++ b/include/video/pm3fb.h @@ -1128,10 +1128,7 @@ #endif /* max number of simultaneous board */ -/* warning : make sure module array def's are coherent with PM3_MAX_BOARD */ #define PM3_MAX_BOARD 4 -#define PM3_MAX_BOARD_MODULE_ARRAY_SHORT "1-4h" -#define PM3_MAX_BOARD_MODULE_ARRAY_STRING "1-4s" /* max size of options */ #define PM3_OPTIONS_SIZE 256 diff --git a/init/Kconfig b/init/Kconfig index 1d19fd25204b..05951c1d654e 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -470,15 +470,6 @@ config MODULE_FORCE_UNLOAD rmmod). This is mainly for kernel developers and desperate users. If unsure, say N. -config OBSOLETE_MODPARM - bool - default y - depends on MODULES - help - You need this option to use module parameters on modules which - have not been converted to the new module parameter system yet. - If unsure, say Y. - config MODVERSIONS bool "Module versioning support" depends on MODULES diff --git a/kernel/module.c b/kernel/module.c index 54623c714bba..ddfe45ac2fd1 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -233,24 +233,6 @@ static unsigned long __find_symbol(const char *name, return 0; } -/* Find a symbol in this elf symbol table */ -static unsigned long find_local_symbol(Elf_Shdr *sechdrs, - unsigned int symindex, - const char *strtab, - const char *name) -{ - unsigned int i; - Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; - - /* Search (defined) internal symbols first. */ - for (i = 1; i < sechdrs[symindex].sh_size/sizeof(*sym); i++) { - if (sym[i].st_shndx != SHN_UNDEF - && strcmp(name, strtab + sym[i].st_name) == 0) - return sym[i].st_value; - } - return 0; -} - /* Search for module by name: must hold module_mutex. */ static struct module *find_module(const char *name) { @@ -785,139 +767,6 @@ static struct module_attribute *modinfo_attrs[] = { NULL, }; -#ifdef CONFIG_OBSOLETE_MODPARM -/* Bounds checking done below */ -static int obsparm_copy_string(const char *val, struct kernel_param *kp) -{ - strcpy(kp->arg, val); - return 0; -} - -static int set_obsolete(const char *val, struct kernel_param *kp) -{ - unsigned int min, max; - unsigned int size, maxsize; - int dummy; - char *endp; - const char *p; - struct obsolete_modparm *obsparm = kp->arg; - - if (!val) { - printk(KERN_ERR "Parameter %s needs an argument\n", kp->name); - return -EINVAL; - } - - /* type is: [min[-max]]{b,h,i,l,s} */ - p = obsparm->type; - min = simple_strtol(p, &endp, 10); - if (endp == obsparm->type) - min = max = 1; - else if (*endp == '-') { - p = endp+1; - max = simple_strtol(p, &endp, 10); - } else - max = min; - switch (*endp) { - case 'b': - return param_array(kp->name, val, min, max, obsparm->addr, - 1, param_set_byte, &dummy); - case 'h': - return param_array(kp->name, val, min, max, obsparm->addr, - sizeof(short), param_set_short, &dummy); - case 'i': - return param_array(kp->name, val, min, max, obsparm->addr, - sizeof(int), param_set_int, &dummy); - case 'l': - return param_array(kp->name, val, min, max, obsparm->addr, - sizeof(long), param_set_long, &dummy); - case 's': - return param_array(kp->name, val, min, max, obsparm->addr, - sizeof(char *), param_set_charp, &dummy); - - case 'c': - /* Undocumented: 1-5c50 means 1-5 strings of up to 49 chars, - and the decl is "char xxx[5][50];" */ - p = endp+1; - maxsize = simple_strtol(p, &endp, 10); - /* We check lengths here (yes, this is a hack). */ - p = val; - while (p[size = strcspn(p, ",")]) { - if (size >= maxsize) - goto oversize; - p += size+1; - } - if (size >= maxsize) - goto oversize; - return param_array(kp->name, val, min, max, obsparm->addr, - maxsize, obsparm_copy_string, &dummy); - } - printk(KERN_ERR "Unknown obsolete parameter type %s\n", obsparm->type); - return -EINVAL; - oversize: - printk(KERN_ERR - "Parameter %s doesn't fit in %u chars.\n", kp->name, maxsize); - return -EINVAL; -} - -static int obsolete_params(const char *name, - char *args, - struct obsolete_modparm obsparm[], - unsigned int num, - Elf_Shdr *sechdrs, - unsigned int symindex, - const char *strtab) -{ - struct kernel_param *kp; - unsigned int i; - int ret; - - kp = kmalloc(sizeof(kp[0]) * num, GFP_KERNEL); - if (!kp) - return -ENOMEM; - - for (i = 0; i < num; i++) { - char sym_name[128 + sizeof(MODULE_SYMBOL_PREFIX)]; - - snprintf(sym_name, sizeof(sym_name), "%s%s", - MODULE_SYMBOL_PREFIX, obsparm[i].name); - - kp[i].name = obsparm[i].name; - kp[i].perm = 000; - kp[i].set = set_obsolete; - kp[i].get = NULL; - obsparm[i].addr - = (void *)find_local_symbol(sechdrs, symindex, strtab, - sym_name); - if (!obsparm[i].addr) { - printk("%s: falsely claims to have parameter %s\n", - name, obsparm[i].name); - ret = -EINVAL; - goto out; - } - kp[i].arg = &obsparm[i]; - } - - ret = parse_args(name, args, kp, num, NULL); - out: - kfree(kp); - return ret; -} -#else -static int obsolete_params(const char *name, - char *args, - struct obsolete_modparm obsparm[], - unsigned int num, - Elf_Shdr *sechdrs, - unsigned int symindex, - const char *strtab) -{ - if (num != 0) - printk(KERN_WARNING "%s: Ignoring obsolete parameters\n", - name); - return 0; -} -#endif /* CONFIG_OBSOLETE_MODPARM */ - static const char vermagic[] = VERMAGIC_STRING; #ifdef CONFIG_MODVERSIONS @@ -1874,27 +1723,17 @@ static struct module *load_module(void __user *umod, set_fs(old_fs); mod->args = args; - if (obsparmindex) { - err = obsolete_params(mod->name, mod->args, - (struct obsolete_modparm *) - sechdrs[obsparmindex].sh_addr, - sechdrs[obsparmindex].sh_size - / sizeof(struct obsolete_modparm), - sechdrs, symindex, - (char *)sechdrs[strindex].sh_addr); - if (setupindex) - printk(KERN_WARNING "%s: Ignoring new-style " - "parameters in presence of obsolete ones\n", - mod->name); - } else { - /* Size of section 0 is 0, so this works well if no params */ - err = parse_args(mod->name, mod->args, - (struct kernel_param *) - sechdrs[setupindex].sh_addr, - sechdrs[setupindex].sh_size - / sizeof(struct kernel_param), - NULL); - } + if (obsparmindex) + printk(KERN_WARNING "%s: Ignoring obsolete parameters\n", + mod->name); + + /* Size of section 0 is 0, so this works well if no params */ + err = parse_args(mod->name, mod->args, + (struct kernel_param *) + sechdrs[setupindex].sh_addr, + sechdrs[setupindex].sh_size + / sizeof(struct kernel_param), + NULL); if (err < 0) goto arch_cleanup; diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 9a1fa8894b95..b4b362b5baf5 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c @@ -54,15 +54,15 @@ static int verbose; /* Print more debug info. */ static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/ -MODULE_PARM(nreaders, "i"); +module_param(nreaders, int, 0); MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); -MODULE_PARM(stat_interval, "i"); +module_param(stat_interval, int, 0); MODULE_PARM_DESC(stat_interval, "Number of seconds between stats printk()s"); -MODULE_PARM(verbose, "i"); +module_param(verbose, bool, 0); MODULE_PARM_DESC(verbose, "Enable verbose debugging printk()s"); -MODULE_PARM(test_no_idle_hz, "i"); +module_param(test_no_idle_hz, bool, 0); MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); -MODULE_PARM(shuffle_interval, "i"); +module_param(shuffle_interval, int, 0); MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); #define TORTURE_FLAG "rcutorture: " #define PRINTK_STRING(s) \ diff --git a/sound/oss/au1000.c b/sound/oss/au1000.c index fe54de25aafc..eacb0aef21e1 100644 --- a/sound/oss/au1000.c +++ b/sound/oss/au1000.c @@ -100,7 +100,7 @@ /* Boot options */ static int vra = 0; // 0 = no VRA, 1 = use VRA if codec supports it -MODULE_PARM(vra, "i"); +module_param(vra, bool, 0); MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it"); diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c index 6a4956b8025d..c1168fae6be6 100644 --- a/sound/oss/au1550_ac97.c +++ b/sound/oss/au1550_ac97.c @@ -79,7 +79,7 @@ * 0 = no VRA, 1 = use VRA if codec supports it */ static int vra = 1; -MODULE_PARM(vra, "i"); +module_param(vra, bool, 0); MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it"); static struct au1550_state { diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c index c9302a1e515b..87bd3100aef3 100644 --- a/sound/oss/dmasound/dmasound_core.c +++ b/sound/oss/dmasound/dmasound_core.c @@ -195,18 +195,18 @@ */ int dmasound_catchRadius = 0; -MODULE_PARM(dmasound_catchRadius, "i"); +module_param(dmasound_catchRadius, int, 0); static unsigned int numWriteBufs = DEFAULT_N_BUFFERS; -MODULE_PARM(numWriteBufs, "i"); +module_param(numWriteBufs, int, 0); static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ; /* in bytes */ -MODULE_PARM(writeBufSize, "i"); +module_param(writeBufSize, int, 0); #ifdef HAS_RECORD static unsigned int numReadBufs = DEFAULT_N_BUFFERS; -MODULE_PARM(numReadBufs, "i"); +module_param(numReadBufs, int, 0); static unsigned int readBufSize = DEFAULT_BUFF_SIZE; /* in bytes */ -MODULE_PARM(readBufSize, "i"); +module_param(readBufSize, int, 0); #endif MODULE_LICENSE("GPL"); diff --git a/sound/oss/ite8172.c b/sound/oss/ite8172.c index ffcb910f5c3e..00ac1c95a429 100644 --- a/sound/oss/ite8172.c +++ b/sound/oss/ite8172.c @@ -1968,9 +1968,9 @@ static int i2s_fmt[NR_DEVICE]; static unsigned int devindex; -MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i"); +module_param_array(spdif, int, NULL, 0); MODULE_PARM_DESC(spdif, "if 1 the S/PDIF digital output is enabled"); -MODULE_PARM(i2s_fmt, "1-" __MODULE_STRING(NR_DEVICE) "i"); +module_param_array(i2s_fmt, int, NULL, 0); MODULE_PARM_DESC(i2s_fmt, "the format of I2S"); MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com"); diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c index dce9016cbcfd..eb5ea32fd1b0 100644 --- a/sound/oss/swarm_cs4297a.c +++ b/sound/oss/swarm_cs4297a.c @@ -154,8 +154,8 @@ static void start_adc(struct cs4297a_state *s); #if CSDEBUG static unsigned long cs_debuglevel = 4; // levels range from 1-9 static unsigned long cs_debugmask = CS_INIT /*| CS_IOCTL*/; -MODULE_PARM(cs_debuglevel, "i"); -MODULE_PARM(cs_debugmask, "i"); +module_param(cs_debuglevel, int, 0); +module_param(cs_debugmask, int, 0); #endif #define CS_TRUE 1 #define CS_FALSE 0 diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c index 99d04ad3ca13..afcb524a40eb 100644 --- a/sound/oss/waveartist.c +++ b/sound/oss/waveartist.c @@ -2028,8 +2028,8 @@ __setup("waveartist=", setup_waveartist); #endif MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver"); -MODULE_PARM(io, "i"); /* IO base */ -MODULE_PARM(irq, "i"); /* IRQ */ -MODULE_PARM(dma, "i"); /* DMA */ -MODULE_PARM(dma2, "i"); /* DMA2 */ +module_param(io, int, 0); /* IO base */ +module_param(irq, int, 0); /* IRQ */ +module_param(dma, int, 0); /* DMA */ +module_param(dma2, int, 0); /* DMA2 */ MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 9871728b756646e0d758a966ba00f2c0ff812817 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sat, 25 Mar 2006 03:07:06 -0800 Subject: [PATCH] kernel/params.c: make param_array() static param_array() in kernel/params.c can now become static. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/moduleparam.h | 7 ------- kernel/params.c | 12 ++++++------ 2 files changed, 6 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index b5c98c43779e..7c0c2c198f1f 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -162,13 +162,6 @@ extern int param_array_get(char *buffer, struct kernel_param *kp); extern int param_set_copystring(const char *val, struct kernel_param *kp); extern int param_get_string(char *buffer, struct kernel_param *kp); -int param_array(const char *name, - const char *val, - unsigned int min, unsigned int max, - void *elem, int elemsize, - int (*set)(const char *, struct kernel_param *kp), - int *num); - /* for exporting parameters in /sys/parameters */ struct module; diff --git a/kernel/params.c b/kernel/params.c index a29150582310..9de637a5c8bc 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -265,12 +265,12 @@ int param_get_invbool(char *buffer, struct kernel_param *kp) } /* We cheat here and temporarily mangle the string. */ -int param_array(const char *name, - const char *val, - unsigned int min, unsigned int max, - void *elem, int elemsize, - int (*set)(const char *, struct kernel_param *kp), - int *num) +static int param_array(const char *name, + const char *val, + unsigned int min, unsigned int max, + void *elem, int elemsize, + int (*set)(const char *, struct kernel_param *kp), + int *num) { int ret; struct kernel_param kp; -- cgit v1.2.3 From c32ccd87bfd1414b0aabfcd8dbc7539ad23bcbaa Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sat, 25 Mar 2006 03:07:09 -0800 Subject: [PATCH] inotify: lock avoidance with parent watch status in dentry Previous inotify work avoidance is good when inotify is completely unused, but it breaks down if even a single watch is in place anywhere in the system. Robin Holt notices that udev is one such culprit - it slows down a 512-thread application on a 512 CPU system from 6 seconds to 22 minutes. Solve this by adding a flag in the dentry that tells inotify whether or not its parent inode has a watch on it. Event queueing to parent will skip taking locks if this flag is cleared. Setting and clearing of this flag on all child dentries versus event delivery: this is no in terms of race cases, and that was shown to be equivalent to always performing the check. The essential behaviour is that activity occuring _after_ a watch has been added and _before_ it has been removed, will generate events. Signed-off-by: Nick Piggin Cc: Robert Love Cc: John McCutchan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dcache.c | 8 +++++ fs/inotify.c | 87 ++++++++++++++++++++++++++++++++++++++++++------ include/linux/dcache.h | 2 ++ include/linux/fsnotify.h | 19 +++++++++++ include/linux/inotify.h | 11 ++++++ 5 files changed, 117 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/dcache.c b/fs/dcache.c index 139e5fd22fa6..0f7ec12d65ff 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -802,6 +802,7 @@ void d_instantiate(struct dentry *entry, struct inode * inode) if (inode) list_add(&entry->d_alias, &inode->i_dentry); entry->d_inode = inode; + fsnotify_d_instantiate(entry, inode); spin_unlock(&dcache_lock); security_d_instantiate(entry, inode); } @@ -853,6 +854,7 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode) list_add(&entry->d_alias, &inode->i_dentry); do_negative: entry->d_inode = inode; + fsnotify_d_instantiate(entry, inode); spin_unlock(&dcache_lock); security_d_instantiate(entry, inode); return NULL; @@ -983,6 +985,7 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) new = __d_find_alias(inode, 1); if (new) { BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED)); + fsnotify_d_instantiate(new, inode); spin_unlock(&dcache_lock); security_d_instantiate(new, inode); d_rehash(dentry); @@ -992,6 +995,7 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) /* d_instantiate takes dcache_lock, so we do it by hand */ list_add(&dentry->d_alias, &inode->i_dentry); dentry->d_inode = inode; + fsnotify_d_instantiate(dentry, inode); spin_unlock(&dcache_lock); security_d_instantiate(dentry, inode); d_rehash(dentry); @@ -1176,6 +1180,9 @@ void d_delete(struct dentry * dentry) spin_lock(&dentry->d_lock); isdir = S_ISDIR(dentry->d_inode->i_mode); if (atomic_read(&dentry->d_count) == 1) { + /* remove this and other inotify debug checks after 2.6.18 */ + dentry->d_flags &= ~DCACHE_INOTIFY_PARENT_WATCHED; + dentry_iput(dentry); fsnotify_nameremove(dentry, isdir); return; @@ -1342,6 +1349,7 @@ already_unhashed: list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs); spin_unlock(&target->d_lock); + fsnotify_d_move(dentry); spin_unlock(&dentry->d_lock); write_sequnlock(&rename_lock); spin_unlock(&dcache_lock); diff --git a/fs/inotify.c b/fs/inotify.c index 0ee39ef591c6..a61e93e17853 100644 --- a/fs/inotify.c +++ b/fs/inotify.c @@ -38,7 +38,6 @@ #include static atomic_t inotify_cookie; -static atomic_t inotify_watches; static kmem_cache_t *watch_cachep; static kmem_cache_t *event_cachep; @@ -380,6 +379,48 @@ static int find_inode(const char __user *dirname, struct nameidata *nd, return error; } +/* + * inotify_inode_watched - returns nonzero if there are watches on this inode + * and zero otherwise. We call this lockless, we do not care if we race. + */ +static inline int inotify_inode_watched(struct inode *inode) +{ + return !list_empty(&inode->inotify_watches); +} + +/* + * Get child dentry flag into synch with parent inode. + * Flag should always be clear for negative dentrys. + */ +static void set_dentry_child_flags(struct inode *inode, int watched) +{ + struct dentry *alias; + + spin_lock(&dcache_lock); + list_for_each_entry(alias, &inode->i_dentry, d_alias) { + struct dentry *child; + + list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) { + if (!child->d_inode) { + WARN_ON(child->d_flags & DCACHE_INOTIFY_PARENT_WATCHED); + continue; + } + spin_lock(&child->d_lock); + if (watched) { + WARN_ON(child->d_flags & + DCACHE_INOTIFY_PARENT_WATCHED); + child->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; + } else { + WARN_ON(!(child->d_flags & + DCACHE_INOTIFY_PARENT_WATCHED)); + child->d_flags&=~DCACHE_INOTIFY_PARENT_WATCHED; + } + spin_unlock(&child->d_lock); + } + } + spin_unlock(&dcache_lock); +} + /* * create_watch - creates a watch on the given device. * @@ -426,7 +467,6 @@ static struct inotify_watch *create_watch(struct inotify_device *dev, get_inotify_watch(watch); atomic_inc(&dev->user->inotify_watches); - atomic_inc(&inotify_watches); return watch; } @@ -458,8 +498,10 @@ static void remove_watch_no_event(struct inotify_watch *watch, list_del(&watch->i_list); list_del(&watch->d_list); + if (!inotify_inode_watched(watch->inode)) + set_dentry_child_flags(watch->inode, 0); + atomic_dec(&dev->user->inotify_watches); - atomic_dec(&inotify_watches); idr_remove(&dev->idr, watch->wd); put_inotify_watch(watch); } @@ -481,16 +523,39 @@ static void remove_watch(struct inotify_watch *watch,struct inotify_device *dev) remove_watch_no_event(watch, dev); } +/* Kernel API */ + /* - * inotify_inode_watched - returns nonzero if there are watches on this inode - * and zero otherwise. We call this lockless, we do not care if we race. + * inotify_d_instantiate - instantiate dcache entry for inode */ -static inline int inotify_inode_watched(struct inode *inode) +void inotify_d_instantiate(struct dentry *entry, struct inode *inode) { - return !list_empty(&inode->inotify_watches); + struct dentry *parent; + + if (!inode) + return; + + WARN_ON(entry->d_flags & DCACHE_INOTIFY_PARENT_WATCHED); + spin_lock(&entry->d_lock); + parent = entry->d_parent; + if (inotify_inode_watched(parent->d_inode)) + entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; + spin_unlock(&entry->d_lock); } -/* Kernel API */ +/* + * inotify_d_move - dcache entry has been moved + */ +void inotify_d_move(struct dentry *entry) +{ + struct dentry *parent; + + parent = entry->d_parent; + if (inotify_inode_watched(parent->d_inode)) + entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; + else + entry->d_flags &= ~DCACHE_INOTIFY_PARENT_WATCHED; +} /** * inotify_inode_queue_event - queue an event to all watches on this inode @@ -538,7 +603,7 @@ void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask, struct dentry *parent; struct inode *inode; - if (!atomic_read (&inotify_watches)) + if (!(dentry->d_flags & DCACHE_INOTIFY_PARENT_WATCHED)) return; spin_lock(&dentry->d_lock); @@ -993,6 +1058,9 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) goto out; } + if (!inotify_inode_watched(inode)) + set_dentry_child_flags(inode, 1); + /* Add the watch to the device's and the inode's list */ list_add(&watch->d_list, &dev->watches); list_add(&watch->i_list, &inode->inotify_watches); @@ -1065,7 +1133,6 @@ static int __init inotify_setup(void) inotify_max_user_watches = 8192; atomic_set(&inotify_cookie, 0); - atomic_set(&inotify_watches, 0); watch_cachep = kmem_cache_create("inotify_watch_cache", sizeof(struct inotify_watch), diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 4361f3789975..d10bd30c337e 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -162,6 +162,8 @@ d_iput: no no no yes #define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */ #define DCACHE_UNHASHED 0x0010 +#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 /* Parent inode is watched */ + extern spinlock_t dcache_lock; /** diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 03b8e7932b83..f7e517c1f1bd 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -16,6 +16,25 @@ #include #include +/* + * fsnotify_d_instantiate - instantiate a dentry for inode + * Called with dcache_lock held. + */ +static inline void fsnotify_d_instantiate(struct dentry *entry, + struct inode *inode) +{ + inotify_d_instantiate(entry, inode); +} + +/* + * fsnotify_d_move - entry has been moved + * Called with dcache_lock and entry->d_lock held. + */ +static inline void fsnotify_d_move(struct dentry *entry) +{ + inotify_d_move(entry); +} + /* * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir */ diff --git a/include/linux/inotify.h b/include/linux/inotify.h index 267c88b5f742..09e00433c78e 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -71,6 +71,8 @@ struct inotify_event { #ifdef CONFIG_INOTIFY +extern void inotify_d_instantiate(struct dentry *, struct inode *); +extern void inotify_d_move(struct dentry *); extern void inotify_inode_queue_event(struct inode *, __u32, __u32, const char *); extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32, @@ -81,6 +83,15 @@ extern u32 inotify_get_cookie(void); #else +static inline void inotify_d_instantiate(struct dentry *dentry, + struct inode *inode) +{ +} + +static inline void inotify_d_move(struct dentry *dentry) +{ +} + static inline void inotify_inode_queue_event(struct inode *inode, __u32 mask, __u32 cookie, const char *filename) -- cgit v1.2.3 From e6a6784627483381d012b507bb0d49809658a1fa Mon Sep 17 00:00:00 2001 From: Rene Herman Date: Sat, 25 Mar 2006 03:07:13 -0800 Subject: [PATCH] parport: move PP_MAJOR from ppdev.h to major.h Today I wondered about /dev/parport after not seeing anything in drivers/parport register char-major-99. Having PP_MAJOR in include/linux/major.h would've allowed me to more quickly determine that it was the ppdev driver driving these. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ppdev.c | 3 ++- include/linux/major.h | 1 + include/linux/ppdev.h | 2 -- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 306ee0f091a4..bee6c47b45bd 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -65,10 +65,11 @@ #include #include #include -#include +#include #include #include #include +#include #define PP_VERSION "ppdev: user-space parallel port driver" #define CHRDEV "ppdev" diff --git a/include/linux/major.h b/include/linux/major.h index e36a46702d94..0a74c52924c9 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -113,6 +113,7 @@ #define UBD_MAJOR 98 +#define PP_MAJOR 99 #define JSFD_MAJOR 99 #define PHONE_MAJOR 100 diff --git a/include/linux/ppdev.h b/include/linux/ppdev.h index 141c96586824..f376a7598a78 100644 --- a/include/linux/ppdev.h +++ b/include/linux/ppdev.h @@ -14,8 +14,6 @@ * Added PPGETMODES/PPGETMODE/PPGETPHASE, Fred Barnes , 03/01/2001 */ -#define PP_MAJOR 99 - #define PP_IOCTL 'p' /* Set mode for read/write (e.g. IEEE1284_MODE_EPP) */ -- cgit v1.2.3 From cd02b966bfcad12d1b2e265dc8dbc331d4c184c4 Mon Sep 17 00:00:00 2001 From: "Vladimir V. Saveliev" Date: Sat, 25 Mar 2006 03:07:15 -0800 Subject: [PATCH] reiserfs: cleanups Clean up several places where gcc issues warnings when -W is specified. Thanks to Neil for finding that. Signed-off-by: Vladimir V. Saveliev Cc: Neil Brown Signed-off-by: Hans Reiser Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/fix_node.c | 4 +--- fs/reiserfs/super.c | 8 ++++---- include/linux/reiserfs_xattr.h | 6 +++--- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/reiserfs/fix_node.c b/fs/reiserfs/fix_node.c index aa22588019ec..5600d3d60cf7 100644 --- a/fs/reiserfs/fix_node.c +++ b/fs/reiserfs/fix_node.c @@ -191,9 +191,7 @@ static void create_virtual_node(struct tree_balance *tb, int h) "vs-8045: create_virtual_node: rdkey %k, affected item==%d (mode==%c) Must be %c", key, vn->vn_affected_item_num, vn->vn_mode, M_DELETE); - } else - /* we can delete directory item, that has only one directory entry in it */ - ; + } } #endif diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 93e6ef9360e3..cae2abbc0c71 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -685,14 +685,14 @@ static const arg_desc_t logging_mode[] = { (1 << REISERFS_DATA_ORDERED | 1 << REISERFS_DATA_WRITEBACK)}, {"writeback", 1 << REISERFS_DATA_WRITEBACK, (1 << REISERFS_DATA_ORDERED | 1 << REISERFS_DATA_LOG)}, - {NULL, 0} + {.value = NULL} }; /* possible values for -o barrier= */ static const arg_desc_t barrier_mode[] = { {"none", 1 << REISERFS_BARRIER_NONE, 1 << REISERFS_BARRIER_FLUSH}, {"flush", 1 << REISERFS_BARRIER_FLUSH, 1 << REISERFS_BARRIER_NONE}, - {NULL, 0} + {.value = NULL} }; /* possible values for "-o block-allocator=" and bits which are to be set in @@ -890,7 +890,7 @@ static int reiserfs_parse_options(struct super_block *s, char *options, /* strin {"acl",.setmask = 1 << REISERFS_UNSUPPORTED_OPT}, {"noacl",.clrmask = 1 << REISERFS_UNSUPPORTED_OPT}, #endif - {"nolog",}, /* This is unsupported */ + {.option_name = "nolog"}, {"replayonly",.setmask = 1 << REPLAYONLY}, {"block-allocator",.arg_required = 'a',.values = balloc}, {"data",.arg_required = 'd',.values = logging_mode}, @@ -908,7 +908,7 @@ static int reiserfs_parse_options(struct super_block *s, char *options, /* strin {"grpjquota",.arg_required = 'g' | (1 << REISERFS_OPT_ALLOWEMPTY),.values = NULL}, {"jqfmt",.arg_required = 'f',.values = NULL}, - {NULL,} + {.option_name = NULL} }; *blocks = 0; diff --git a/include/linux/reiserfs_xattr.h b/include/linux/reiserfs_xattr.h index 87280eb6083d..5353afb11db3 100644 --- a/include/linux/reiserfs_xattr.h +++ b/include/linux/reiserfs_xattr.h @@ -101,13 +101,13 @@ static inline void reiserfs_mark_inode_private(struct inode *inode) #else #define is_reiserfs_priv_object(inode) 0 -#define reiserfs_mark_inode_private(inode) +#define reiserfs_mark_inode_private(inode) do {;} while(0) #define reiserfs_getxattr NULL #define reiserfs_setxattr NULL #define reiserfs_listxattr NULL #define reiserfs_removexattr NULL -#define reiserfs_write_lock_xattrs(sb) -#define reiserfs_write_unlock_xattrs(sb) +#define reiserfs_write_lock_xattrs(sb) do {;} while(0) +#define reiserfs_write_unlock_xattrs(sb) do {;} while(0) #define reiserfs_read_lock_xattrs(sb) #define reiserfs_read_unlock_xattrs(sb) -- cgit v1.2.3 From e51c01b08474ea454a965a937fff0407ab6714c7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 25 Mar 2006 03:07:17 -0800 Subject: [PATCH] hp300: fix driver_register() return handling, remove dio_module_init() Remove the assumption that driver_register() returns the number of devices bound to the driver. In fact, it returns zero for success or a negative error value. dio_module_init() used the device count to automatically unregister and unload drivers that found no devices. That might have worked at one time, but has been broken for some time because dio_register_driver() returned either a negative error or a positive count (never zero). So it could only unregister on failure, when it's not needed anyway. This functionality could be resurrected in individual drivers by counting devices in their .probe() methods. Signed-off-by: Bjorn Helgaas Cc: Philip Blundell Cc: Jochen Friedrich Cc: "Antonino A. Daplas" Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/dio/dio-driver.c | 9 ++------- drivers/net/hplance.c | 2 +- drivers/serial/8250_hp300.c | 10 +++++----- drivers/video/hpfb.c | 4 +++- include/linux/dio.h | 32 -------------------------------- 5 files changed, 11 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/dio/dio-driver.c b/drivers/dio/dio-driver.c index ca8e69d2f64d..e4c48e329367 100644 --- a/drivers/dio/dio-driver.c +++ b/drivers/dio/dio-driver.c @@ -71,22 +71,17 @@ static int dio_device_probe(struct device *dev) * @drv: the driver structure to register * * Adds the driver structure to the list of registered drivers - * Returns the number of DIO devices which were claimed by the driver - * during registration. The driver remains registered even if the - * return value is zero. + * Returns zero or a negative error value. */ int dio_register_driver(struct dio_driver *drv) { - int count = 0; - /* initialize common driver fields */ drv->driver.name = drv->name; drv->driver.bus = &dio_bus_type; /* register with core */ - count = driver_register(&drv->driver); - return count ? count : 1; + return driver_register(&drv->driver); } diff --git a/drivers/net/hplance.c b/drivers/net/hplance.c index d8410634bcaf..685693464605 100644 --- a/drivers/net/hplance.c +++ b/drivers/net/hplance.c @@ -217,7 +217,7 @@ static int hplance_close(struct net_device *dev) int __init hplance_init_module(void) { - return dio_module_init(&hplance_driver); + return dio_register_driver(&hplance_driver); } void __exit hplance_cleanup_module(void) diff --git a/drivers/serial/8250_hp300.c b/drivers/serial/8250_hp300.c index 4315afe9c080..53e81a44c1a3 100644 --- a/drivers/serial/8250_hp300.c +++ b/drivers/serial/8250_hp300.c @@ -55,6 +55,8 @@ static struct dio_driver hpdca_driver = { #endif +static unsigned int num_ports; + extern int hp300_uart_scode; /* Offset to UART registers from base of DCA */ @@ -199,6 +201,8 @@ static int __devinit hpdca_init_one(struct dio_dev *d, out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff); udelay(100); + num_ports++; + return 0; } #endif @@ -206,7 +210,6 @@ static int __devinit hpdca_init_one(struct dio_dev *d, static int __init hp300_8250_init(void) { static int called = 0; - int num_ports; #ifdef CONFIG_HPAPCI int line; unsigned long base; @@ -221,11 +224,8 @@ static int __init hp300_8250_init(void) if (!MACH_IS_HP300) return -ENODEV; - num_ports = 0; - #ifdef CONFIG_HPDCA - if (dio_module_init(&hpdca_driver) == 0) - num_ports++; + dio_register_driver(&hpdca_driver); #endif #ifdef CONFIG_HPAPCI if (hp300_model < HP_400) { diff --git a/drivers/video/hpfb.c b/drivers/video/hpfb.c index bebdac59d231..abd920a663a0 100644 --- a/drivers/video/hpfb.c +++ b/drivers/video/hpfb.c @@ -386,7 +386,9 @@ int __init hpfb_init(void) if (fb_get_options("hpfb", NULL)) return -ENODEV; - dio_module_init(&hpfb_driver); + err = dio_register_driver(&hpfb_driver); + if (err) + return err; fs = get_fs(); set_fs(KERNEL_DS); diff --git a/include/linux/dio.h b/include/linux/dio.h index fae9395fcf4f..1e65ebc2a3db 100644 --- a/include/linux/dio.h +++ b/include/linux/dio.h @@ -276,37 +276,5 @@ static inline void dio_set_drvdata (struct dio_dev *d, void *data) dev_set_drvdata(&d->dev, data); } -/* - * A helper function which helps ensure correct dio_driver - * setup and cleanup for commonly-encountered hotplug/modular cases - * - * This MUST stay in a header, as it checks for -DMODULE - */ -static inline int dio_module_init(struct dio_driver *drv) -{ - int rc = dio_register_driver(drv); - - if (rc > 0) - return 0; - - /* iff CONFIG_HOTPLUG and built into kernel, we should - * leave the driver around for future hotplug events. - * For the module case, a hotplug daemon of some sort - * should load a module in response to an insert event. */ -#if defined(CONFIG_HOTPLUG) && !defined(MODULE) - if (rc == 0) - return 0; -#else - if (rc == 0) - rc = -ENODEV; -#endif - - /* if we get here, we need to clean up DIO driver instance - * and return some sort of error */ - dio_unregister_driver(drv); - - return rc; -} - #endif /* __KERNEL__ */ #endif /* ndef _LINUX_DIO_H */ -- cgit v1.2.3 From 33d8675ea66e79d21da3ed64ce88dfb2a18bc6a7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 25 Mar 2006 03:07:20 -0800 Subject: [PATCH] amiga: fix driver_register() return handling, remove zorro_module_init() Remove the assumption that driver_register() returns the number of devices bound to the driver. In fact, it returns zero for success or a negative error value. zorro_module_init() used the device count to automatically unregister and unload drivers that found no devices. That might have worked at one time, but has been broken for some time because zorro_register_driver() returned either a negative error or a positive count (never zero). So it could only unregister on failure, when it's not needed anyway. This functionality could be resurrected in individual drivers by counting devices in their .probe() methods. Signed-off-by: Bjorn Helgaas Cc: Geert Uytterhoeven Cc: Roman Zippel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/a2065.c | 2 +- drivers/net/ariadne.c | 2 +- drivers/net/hydra.c | 2 +- drivers/net/zorro8390.c | 2 +- drivers/video/cirrusfb.c | 2 +- drivers/zorro/zorro-driver.c | 9 ++------- include/linux/zorro.h | 33 --------------------------------- 7 files changed, 7 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c index 8e538a6d7d97..79bb56b8dcef 100644 --- a/drivers/net/a2065.c +++ b/drivers/net/a2065.c @@ -829,7 +829,7 @@ static void __devexit a2065_remove_one(struct zorro_dev *z) static int __init a2065_init_module(void) { - return zorro_module_init(&a2065_driver); + return zorro_register_driver(&a2065_driver); } static void __exit a2065_cleanup_module(void) diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c index 9fe93acfc8ef..d1b6b1f794e2 100644 --- a/drivers/net/ariadne.c +++ b/drivers/net/ariadne.c @@ -864,7 +864,7 @@ static void __devexit ariadne_remove_one(struct zorro_dev *z) static int __init ariadne_init_module(void) { - return zorro_module_init(&ariadne_driver); + return zorro_register_driver(&ariadne_driver); } static void __exit ariadne_cleanup_module(void) diff --git a/drivers/net/hydra.c b/drivers/net/hydra.c index 6e0ca7340a8f..d9fb8e74e631 100644 --- a/drivers/net/hydra.c +++ b/drivers/net/hydra.c @@ -242,7 +242,7 @@ static void __devexit hydra_remove_one(struct zorro_dev *z) static int __init hydra_init_module(void) { - return zorro_module_init(&hydra_driver); + return zorro_register_driver(&hydra_driver); } static void __exit hydra_cleanup_module(void) diff --git a/drivers/net/zorro8390.c b/drivers/net/zorro8390.c index 761021603597..8037e5806d0a 100644 --- a/drivers/net/zorro8390.c +++ b/drivers/net/zorro8390.c @@ -426,7 +426,7 @@ static void __devexit zorro8390_remove_one(struct zorro_dev *z) static int __init zorro8390_init_module(void) { - return zorro_module_init(&zorro8390_driver); + return zorro_register_driver(&zorro8390_driver); } static void __exit zorro8390_cleanup_module(void) diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index e0dbdfc0c8b4..66d6f2f0a219 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -2622,7 +2622,7 @@ static int __init cirrusfb_init(void) #endif #ifdef CONFIG_ZORRO - error |= zorro_module_init(&cirrusfb_zorro_driver); + error |= zorro_register_driver(&cirrusfb_zorro_driver); #endif #ifdef CONFIG_PCI error |= pci_register_driver(&cirrusfb_pci_driver); diff --git a/drivers/zorro/zorro-driver.c b/drivers/zorro/zorro-driver.c index fcbee748c592..067c07be928c 100644 --- a/drivers/zorro/zorro-driver.c +++ b/drivers/zorro/zorro-driver.c @@ -65,22 +65,17 @@ static int zorro_device_probe(struct device *dev) * @drv: the driver structure to register * * Adds the driver structure to the list of registered drivers - * Returns the number of Zorro devices which were claimed by the driver - * during registration. The driver remains registered even if the - * return value is zero. + * Returns zero or a negative error value. */ int zorro_register_driver(struct zorro_driver *drv) { - int count = 0; - /* initialize common driver fields */ drv->driver.name = drv->name; drv->driver.bus = &zorro_bus_type; /* register with core */ - count = driver_register(&drv->driver); - return count ? count : 1; + return driver_register(&drv->driver); } diff --git a/include/linux/zorro.h b/include/linux/zorro.h index ba5b72768bbe..2f135cf6eef1 100644 --- a/include/linux/zorro.h +++ b/include/linux/zorro.h @@ -271,39 +271,6 @@ static inline void zorro_set_drvdata (struct zorro_dev *z, void *data) } -/* - * A helper function which helps ensure correct zorro_driver - * setup and cleanup for commonly-encountered hotplug/modular cases - * - * This MUST stay in a header, as it checks for -DMODULE - */ -static inline int zorro_module_init(struct zorro_driver *drv) -{ - int rc = zorro_register_driver(drv); - - if (rc > 0) - return 0; - - /* iff CONFIG_HOTPLUG and built into kernel, we should - * leave the driver around for future hotplug events. - * For the module case, a hotplug daemon of some sort - * should load a module in response to an insert event. */ -#if defined(CONFIG_HOTPLUG) && !defined(MODULE) - if (rc == 0) - return 0; -#else - if (rc == 0) - rc = -ENODEV; -#endif - - /* if we get here, we need to clean up Zorro driver instance - * and return some sort of error */ - zorro_unregister_driver(drv); - - return rc; -} - - /* * Bitmask indicating portions of available Zorro II RAM that are unused * by the system. Every bit represents a 64K chunk, for a maximum of 8MB -- cgit v1.2.3 From c777ac5594f772ac760e02c3ac71d067616b579d Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 25 Mar 2006 03:07:36 -0800 Subject: [PATCH] irq: uninline migration functions Uninline some massive IRQ migration functions. Put them in the new kernel/irq/migration.c. Cc: Andi Kleen Cc: "Luck, Tony" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/irq.h | 49 ++--------------------------------------------- kernel/irq/Makefile | 3 +-- kernel/irq/migration.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 49 deletions(-) create mode 100644 kernel/irq/migration.c (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 6c5d4c898ccb..ee2a82a572f7 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -114,53 +114,8 @@ static inline void set_native_irq_info(int irq, cpumask_t mask) #if defined (CONFIG_GENERIC_PENDING_IRQ) || defined (CONFIG_IRQBALANCE) extern cpumask_t pending_irq_cpumask[NR_IRQS]; -static inline void set_pending_irq(unsigned int irq, cpumask_t mask) -{ - irq_desc_t *desc = irq_desc + irq; - unsigned long flags; - - spin_lock_irqsave(&desc->lock, flags); - desc->move_irq = 1; - pending_irq_cpumask[irq] = mask; - spin_unlock_irqrestore(&desc->lock, flags); -} - -static inline void -move_native_irq(int irq) -{ - cpumask_t tmp; - irq_desc_t *desc = irq_descp(irq); - - if (likely (!desc->move_irq)) - return; - - desc->move_irq = 0; - - if (likely(cpus_empty(pending_irq_cpumask[irq]))) - return; - - if (!desc->handler->set_affinity) - return; - - /* note - we hold the desc->lock */ - cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map); - - /* - * If there was a valid mask to work with, please - * do the disable, re-program, enable sequence. - * This is *not* particularly important for level triggered - * but in a edge trigger case, we might be setting rte - * when an active trigger is comming in. This could - * cause some ioapics to mal-function. - * Being paranoid i guess! - */ - if (unlikely(!cpus_empty(tmp))) { - desc->handler->disable(irq); - desc->handler->set_affinity(irq,tmp); - desc->handler->enable(irq); - } - cpus_clear(pending_irq_cpumask[irq]); -} +void set_pending_irq(unsigned int irq, cpumask_t mask); +void move_native_irq(int irq); #ifdef CONFIG_PCI_MSI /* diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile index 49378738ff5e..2b33f852be3e 100644 --- a/kernel/irq/Makefile +++ b/kernel/irq/Makefile @@ -1,5 +1,4 @@ -obj-y := handle.o manage.o spurious.o +obj-y := handle.o manage.o spurious.o migration.o obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o obj-$(CONFIG_PROC_FS) += proc.o - diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c new file mode 100644 index 000000000000..6bdd03c524c7 --- /dev/null +++ b/kernel/irq/migration.c @@ -0,0 +1,52 @@ +#include + +#if defined(CONFIG_GENERIC_PENDING_IRQ) + +void set_pending_irq(unsigned int irq, cpumask_t mask) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + desc->move_irq = 1; + pending_irq_cpumask[irq] = mask; + spin_unlock_irqrestore(&desc->lock, flags); +} + +void move_native_irq(int irq) +{ + cpumask_t tmp; + irq_desc_t *desc = irq_descp(irq); + + if (likely (!desc->move_irq)) + return; + + desc->move_irq = 0; + + if (likely(cpus_empty(pending_irq_cpumask[irq]))) + return; + + if (!desc->handler->set_affinity) + return; + + /* note - we hold the desc->lock */ + cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map); + + /* + * If there was a valid mask to work with, please + * do the disable, re-program, enable sequence. + * This is *not* particularly important for level triggered + * but in a edge trigger case, we might be setting rte + * when an active trigger is comming in. This could + * cause some ioapics to mal-function. + * Being paranoid i guess! + */ + if (unlikely(!cpus_empty(tmp))) { + desc->handler->disable(irq); + desc->handler->set_affinity(irq,tmp); + desc->handler->enable(irq); + } + cpus_clear(pending_irq_cpumask[irq]); +} + +#endif -- cgit v1.2.3 From f348d70a324e15afc701a494f32ec468abb7d1eb Mon Sep 17 00:00:00 2001 From: Davide Libenzi Date: Sat, 25 Mar 2006 03:07:39 -0800 Subject: [PATCH] POLLRDHUP/EPOLLRDHUP handling for half-closed devices notifications Implement the half-closed devices notifiation, by adding a new POLLRDHUP (and its alias EPOLLRDHUP) bit to the existing poll/select sets. Since the existing POLLHUP handling, that does not report correctly half-closed devices, was feared to be changed, this implementation leaves the current POLLHUP reporting unchanged and simply add a new bit that is set in the few places where it makes sense. The same thing was discussed and conceptually agreed quite some time ago: http://lkml.org/lkml/2003/7/12/116 Since this new event bit is added to the existing Linux poll infrastruture, even the existing poll/select system calls will be able to use it. As far as the existing POLLHUP handling, the patch leaves it as is. The pollrdhup-2.6.16.rc5-0.10.diff defines the POLLRDHUP for all the existing archs and sets the bit in the six relevant files. The other attached diff is the simple change required to sys/epoll.h to add the EPOLLRDHUP definition. There is "a stupid program" to test POLLRDHUP delivery here: http://www.xmailserver.org/pollrdhup-test.c It tests poll(2), but since the delivery is same epoll(2) will work equally. Signed-off-by: Davide Libenzi Cc: "David S. Miller" Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/eventpoll.c | 4 ++-- include/asm-alpha/poll.h | 2 ++ include/asm-arm/poll.h | 1 + include/asm-arm26/poll.h | 1 + include/asm-cris/poll.h | 1 + include/asm-frv/poll.h | 1 + include/asm-h8300/poll.h | 1 + include/asm-i386/poll.h | 1 + include/asm-ia64/poll.h | 1 + include/asm-m32r/poll.h | 1 + include/asm-m68k/poll.h | 1 + include/asm-mips/poll.h | 1 + include/asm-parisc/poll.h | 1 + include/asm-powerpc/poll.h | 1 + include/asm-s390/poll.h | 1 + include/asm-sh/poll.h | 1 + include/asm-sh64/poll.h | 1 + include/asm-sparc/poll.h | 1 + include/asm-sparc64/poll.h | 1 + include/asm-v850/poll.h | 1 + include/asm-x86_64/poll.h | 1 + include/asm-xtensa/poll.h | 1 + net/bluetooth/af_bluetooth.c | 3 +++ net/core/datagram.c | 2 ++ net/dccp/proto.c | 2 +- net/ipv4/tcp.c | 2 +- net/sctp/socket.c | 2 ++ net/unix/af_unix.c | 2 ++ 28 files changed, 35 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 1c2b16fda13a..a0f682cdd03e 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -599,7 +599,7 @@ sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event) switch (op) { case EPOLL_CTL_ADD: if (!epi) { - epds.events |= POLLERR | POLLHUP; + epds.events |= POLLERR | POLLHUP | POLLRDHUP; error = ep_insert(ep, &epds, tfile, fd); } else @@ -613,7 +613,7 @@ sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event) break; case EPOLL_CTL_MOD: if (epi) { - epds.events |= POLLERR | POLLHUP; + epds.events |= POLLERR | POLLHUP | POLLRDHUP; error = ep_modify(ep, epi, &epds); } else error = -ENOENT; diff --git a/include/asm-alpha/poll.h b/include/asm-alpha/poll.h index 34f333b762a0..95707182b3ed 100644 --- a/include/asm-alpha/poll.h +++ b/include/asm-alpha/poll.h @@ -13,6 +13,8 @@ #define POLLWRBAND (1 << 9) #define POLLMSG (1 << 10) #define POLLREMOVE (1 << 11) +#define POLLRDHUP (1 << 12) + struct pollfd { int fd; diff --git a/include/asm-arm/poll.h b/include/asm-arm/poll.h index 2744ca831f5d..5030b2b232a3 100644 --- a/include/asm-arm/poll.h +++ b/include/asm-arm/poll.h @@ -16,6 +16,7 @@ #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-arm26/poll.h b/include/asm-arm26/poll.h index fdfdab064a65..9ccb7f4190ca 100644 --- a/include/asm-arm26/poll.h +++ b/include/asm-arm26/poll.h @@ -15,6 +15,7 @@ #define POLLWRNORM 0x0100 #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-cris/poll.h b/include/asm-cris/poll.h index 1c0efc3e4be7..1b25d4cf498c 100644 --- a/include/asm-cris/poll.h +++ b/include/asm-cris/poll.h @@ -15,6 +15,7 @@ #define POLLWRBAND 512 #define POLLMSG 1024 #define POLLREMOVE 4096 +#define POLLRDHUP 8192 struct pollfd { int fd; diff --git a/include/asm-frv/poll.h b/include/asm-frv/poll.h index 8cbcd60e334f..c8fe8801d075 100644 --- a/include/asm-frv/poll.h +++ b/include/asm-frv/poll.h @@ -12,6 +12,7 @@ #define POLLRDBAND 128 #define POLLWRBAND 256 #define POLLMSG 0x0400 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-h8300/poll.h b/include/asm-h8300/poll.h index bf49ab8ad6da..fc52103b276a 100644 --- a/include/asm-h8300/poll.h +++ b/include/asm-h8300/poll.h @@ -12,6 +12,7 @@ #define POLLRDBAND 128 #define POLLWRBAND 256 #define POLLMSG 0x0400 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-i386/poll.h b/include/asm-i386/poll.h index aecc80a15d36..2cd4929abd40 100644 --- a/include/asm-i386/poll.h +++ b/include/asm-i386/poll.h @@ -16,6 +16,7 @@ #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-ia64/poll.h b/include/asm-ia64/poll.h index 160258a0528d..bcaf9f140242 100644 --- a/include/asm-ia64/poll.h +++ b/include/asm-ia64/poll.h @@ -21,6 +21,7 @@ #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-m32r/poll.h b/include/asm-m32r/poll.h index 43b7acf732d5..9e0e700e727c 100644 --- a/include/asm-m32r/poll.h +++ b/include/asm-m32r/poll.h @@ -21,6 +21,7 @@ #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-m68k/poll.h b/include/asm-m68k/poll.h index c4b69c4a87e1..0fb8843647f8 100644 --- a/include/asm-m68k/poll.h +++ b/include/asm-m68k/poll.h @@ -13,6 +13,7 @@ #define POLLWRBAND 256 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-mips/poll.h b/include/asm-mips/poll.h index a000f1f789e3..70881f8c5c50 100644 --- a/include/asm-mips/poll.h +++ b/include/asm-mips/poll.h @@ -17,6 +17,7 @@ /* These seem to be more or less nonstandard ... */ #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-parisc/poll.h b/include/asm-parisc/poll.h index 1c1da86934cf..20e4d03c74cb 100644 --- a/include/asm-parisc/poll.h +++ b/include/asm-parisc/poll.h @@ -16,6 +16,7 @@ #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-powerpc/poll.h b/include/asm-powerpc/poll.h index edd2054da86b..9c7d12631033 100644 --- a/include/asm-powerpc/poll.h +++ b/include/asm-powerpc/poll.h @@ -13,6 +13,7 @@ #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-s390/poll.h b/include/asm-s390/poll.h index e90a5ca42061..6f7f65ac7d27 100644 --- a/include/asm-s390/poll.h +++ b/include/asm-s390/poll.h @@ -24,6 +24,7 @@ #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-sh/poll.h b/include/asm-sh/poll.h index 52f95b9188dc..dbca9b32f4a6 100644 --- a/include/asm-sh/poll.h +++ b/include/asm-sh/poll.h @@ -16,6 +16,7 @@ #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-sh64/poll.h b/include/asm-sh64/poll.h index a420d14eb704..3a6cbad08d28 100644 --- a/include/asm-sh64/poll.h +++ b/include/asm-sh64/poll.h @@ -26,6 +26,7 @@ #define POLLWRNORM 0x0100 #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-sparc/poll.h b/include/asm-sparc/poll.h index 3ddcc6481f09..26f13fb35497 100644 --- a/include/asm-sparc/poll.h +++ b/include/asm-sparc/poll.h @@ -13,6 +13,7 @@ #define POLLWRBAND 256 #define POLLMSG 512 #define POLLREMOVE 1024 +#define POLLRDHUP 2048 struct pollfd { int fd; diff --git a/include/asm-sparc64/poll.h b/include/asm-sparc64/poll.h index 31b611aa7468..ab6b0d1bb4ad 100644 --- a/include/asm-sparc64/poll.h +++ b/include/asm-sparc64/poll.h @@ -13,6 +13,7 @@ #define POLLWRBAND 256 #define POLLMSG 512 #define POLLREMOVE 1024 +#define POLLRDHUP 2048 struct pollfd { int fd; diff --git a/include/asm-v850/poll.h b/include/asm-v850/poll.h index 0369562c7e15..c10176c2c28f 100644 --- a/include/asm-v850/poll.h +++ b/include/asm-v850/poll.h @@ -13,6 +13,7 @@ #define POLLWRBAND 0x0100 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-x86_64/poll.h b/include/asm-x86_64/poll.h index c43cbba31913..c0475a9d8bb8 100644 --- a/include/asm-x86_64/poll.h +++ b/include/asm-x86_64/poll.h @@ -16,6 +16,7 @@ #define POLLWRBAND 0x0200 #define POLLMSG 0x0400 #define POLLREMOVE 0x1000 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/include/asm-xtensa/poll.h b/include/asm-xtensa/poll.h index dffe447534e0..6fd94773e866 100644 --- a/include/asm-xtensa/poll.h +++ b/include/asm-xtensa/poll.h @@ -27,6 +27,7 @@ #define POLLMSG 0x0400 #define POLLREMOVE 0x0800 +#define POLLRDHUP 0x2000 struct pollfd { int fd; diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index fb031fe9be9e..469eda0f0dfd 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -238,6 +238,9 @@ unsigned int bt_sock_poll(struct file * file, struct socket *sock, poll_table *w if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) mask |= POLLERR; + if (sk->sk_shutdown & RCV_SHUTDOWN) + mask |= POLLRDHUP; + if (sk->sk_shutdown == SHUTDOWN_MASK) mask |= POLLHUP; diff --git a/net/core/datagram.c b/net/core/datagram.c index b8ce6bf81188..aecddcc30401 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -500,6 +500,8 @@ unsigned int datagram_poll(struct file *file, struct socket *sock, /* exceptional events? */ if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) mask |= POLLERR; + if (sk->sk_shutdown & RCV_SHUTDOWN) + mask |= POLLRDHUP; if (sk->sk_shutdown == SHUTDOWN_MASK) mask |= POLLHUP; diff --git a/net/dccp/proto.c b/net/dccp/proto.c index d4b293e16283..1ff7328b0e17 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -350,7 +350,7 @@ unsigned int dccp_poll(struct file *file, struct socket *sock, if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED) mask |= POLLHUP; if (sk->sk_shutdown & RCV_SHUTDOWN) - mask |= POLLIN | POLLRDNORM; + mask |= POLLIN | POLLRDNORM | POLLRDHUP; /* Connected? */ if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 4b0272c92d66..19ea5c0b094b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -365,7 +365,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait) if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE) mask |= POLLHUP; if (sk->sk_shutdown & RCV_SHUTDOWN) - mask |= POLLIN | POLLRDNORM; + mask |= POLLIN | POLLRDNORM | POLLRDHUP; /* Connected? */ if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) { diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 0ea947eb6813..b6e4b89539b3 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4894,6 +4894,8 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait) /* Is there any exceptional events? */ if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) mask |= POLLERR; + if (sk->sk_shutdown & RCV_SHUTDOWN) + mask |= POLLRDHUP; if (sk->sk_shutdown == SHUTDOWN_MASK) mask |= POLLHUP; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 2b4cc2eea5b3..d901465ce013 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1878,6 +1878,8 @@ static unsigned int unix_poll(struct file * file, struct socket *sock, poll_tabl mask |= POLLERR; if (sk->sk_shutdown == SHUTDOWN_MASK) mask |= POLLHUP; + if (sk->sk_shutdown & RCV_SHUTDOWN) + mask |= POLLRDHUP; /* readable? */ if (!skb_queue_empty(&sk->sk_receive_queue) || -- cgit v1.2.3 From 77d47582c2345e071df02afaf9191641009287c4 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sat, 25 Mar 2006 03:07:39 -0800 Subject: [PATCH] add a proper prototype for setup_arch() This patch adds a proper prototype for setup_arch() in init.h. This patch is based on a patch by Ben Dooks . Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/init.h | 4 ++++ init/main.c | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/init.h b/include/linux/init.h index ff8d8b8632f4..ed0ac7c39fdc 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -69,6 +69,10 @@ extern initcall_t __security_initcall_start[], __security_initcall_end[]; /* Defined in init/main.c */ extern char saved_command_line[]; + +/* used by init/main.c */ +extern void setup_arch(char **); + #endif #ifndef MODULE diff --git a/init/main.c b/init/main.c index a596cb8ac982..006dcd547dc2 100644 --- a/init/main.c +++ b/init/main.c @@ -306,8 +306,6 @@ static int __init rdinit_setup(char *str) } __setup("rdinit=", rdinit_setup); -extern void setup_arch(char **); - #ifndef CONFIG_SMP #ifdef CONFIG_X86_LOCAL_APIC -- cgit v1.2.3 From 12b5989be10011387a9da5dee82e5c0d6f9d02e7 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Sat, 25 Mar 2006 03:07:41 -0800 Subject: [PATCH] refactor capable() to one implementation, add __capable() helper Move capable() to kernel/capability.c and eliminate duplicate implementations. Add __capable() function which can be used to check for capabiilty of any process. Signed-off-by: Chris Wright Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/capability.h | 3 ++- include/linux/security.h | 22 ++++++++++++++++------ kernel/capability.c | 16 ++++++++++++++++ kernel/sys.c | 12 ------------ security/security.c | 23 ----------------------- 5 files changed, 34 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/linux/capability.h b/include/linux/capability.h index 5a23ce752629..6548b35ab9f6 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -357,7 +357,8 @@ static inline kernel_cap_t cap_invert(kernel_cap_t c) #define cap_is_fs_cap(c) (CAP_TO_MASK(c) & CAP_FS_MASK) -extern int capable(int cap); +int capable(int cap); +int __capable(struct task_struct *t, int cap); #endif /* __KERNEL__ */ diff --git a/include/linux/security.h b/include/linux/security.h index b18eb8cfa639..3c19be35124b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1040,6 +1040,11 @@ struct swap_info_struct; * @effective contains the effective capability set. * @inheritable contains the inheritable capability set. * @permitted contains the permitted capability set. + * @capable: + * Check whether the @tsk process has the @cap capability. + * @tsk contains the task_struct for the process. + * @cap contains the capability . + * Return 0 if the capability is granted for @tsk. * @acct: * Check permission before enabling or disabling process accounting. If * accounting is being enabled, then @file refers to the open file used to @@ -1053,11 +1058,6 @@ struct swap_info_struct; * @table contains the ctl_table structure for the sysctl variable. * @op contains the operation (001 = search, 002 = write, 004 = read). * Return 0 if permission is granted. - * @capable: - * Check whether the @tsk process has the @cap capability. - * @tsk contains the task_struct for the process. - * @cap contains the capability . - * Return 0 if the capability is granted for @tsk. * @syslog: * Check permission before accessing the kernel message ring or changing * logging to the console. @@ -1099,9 +1099,9 @@ struct security_operations { kernel_cap_t * effective, kernel_cap_t * inheritable, kernel_cap_t * permitted); + int (*capable) (struct task_struct * tsk, int cap); int (*acct) (struct file * file); int (*sysctl) (struct ctl_table * table, int op); - int (*capable) (struct task_struct * tsk, int cap); int (*quotactl) (int cmds, int type, int id, struct super_block * sb); int (*quota_on) (struct dentry * dentry); int (*syslog) (int type); @@ -1347,6 +1347,11 @@ static inline void security_capset_set (struct task_struct *target, security_ops->capset_set (target, effective, inheritable, permitted); } +static inline int security_capable(struct task_struct *tsk, int cap) +{ + return security_ops->capable(tsk, cap); +} + static inline int security_acct (struct file *file) { return security_ops->acct (file); @@ -2050,6 +2055,11 @@ static inline void security_capset_set (struct task_struct *target, cap_capset_set (target, effective, inheritable, permitted); } +static inline int security_capable(struct task_struct *tsk, int cap) +{ + return cap_capable(tsk, cap); +} + static inline int security_acct (struct file *file) { return 0; diff --git a/kernel/capability.c b/kernel/capability.c index bfa3c92e16f2..1a4d8a40d3f9 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -233,3 +233,19 @@ out: return ret; } + +int __capable(struct task_struct *t, int cap) +{ + if (security_capable(t, cap) == 0) { + t->flags |= PF_SUPERPRIV; + return 1; + } + return 0; +} +EXPORT_SYMBOL(__capable); + +int capable(int cap) +{ + return __capable(current, cap); +} +EXPORT_SYMBOL(capable); diff --git a/kernel/sys.c b/kernel/sys.c index 19d058be49d4..421009cedb51 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -224,18 +224,6 @@ int unregister_reboot_notifier(struct notifier_block * nb) EXPORT_SYMBOL(unregister_reboot_notifier); -#ifndef CONFIG_SECURITY -int capable(int cap) -{ - if (cap_raised(current->cap_effective, cap)) { - current->flags |= PF_SUPERPRIV; - return 1; - } - return 0; -} -EXPORT_SYMBOL(capable); -#endif - static int set_one_prio(struct task_struct *p, int niceval, int error) { int no_nice; diff --git a/security/security.c b/security/security.c index f693e1f66b98..51ef509710b9 100644 --- a/security/security.c +++ b/security/security.c @@ -174,31 +174,8 @@ int mod_unreg_security(const char *name, struct security_operations *ops) return security_ops->unregister_security(name, ops); } -/** - * capable - calls the currently loaded security module's capable() function with the specified capability - * @cap: the requested capability level. - * - * This function calls the currently loaded security module's capable() - * function with a pointer to the current task and the specified @cap value. - * - * This allows the security module to implement the capable function call - * however it chooses to. - */ -int capable(int cap) -{ - if (security_ops->capable(current, cap)) { - /* capability denied */ - return 0; - } - - /* capability granted */ - current->flags |= PF_SUPERPRIV; - return 1; -} - EXPORT_SYMBOL_GPL(register_security); EXPORT_SYMBOL_GPL(unregister_security); EXPORT_SYMBOL_GPL(mod_reg_security); EXPORT_SYMBOL_GPL(mod_unreg_security); -EXPORT_SYMBOL(capable); EXPORT_SYMBOL(security_ops); -- cgit v1.2.3 From 962749af67b145c57917bfbff3c303ebd7d5988c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 25 Mar 2006 03:08:01 -0800 Subject: [PATCH] roundup_pow_of_two() 64-bit fix fls() takes an integer, so roundup_pow_of_two() is busted for ulongs larger than 2^32-1. Fix this by implementing and using fls_long(). (Why does roundup_pow_of_two() return a long?) (Why is roundup_pow_of_two() __attribute_const__ whereas long_log2() is __attribute_pure__?) (Why does long_log2() suck so much? Because we were missing fls_long()?) Cc: Roland Dreier Cc: "Chen, Kenneth W" Cc: John Hawkes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bitops.h | 7 +++++++ include/linux/kernel.h | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/bitops.h b/include/linux/bitops.h index 208650b1ad3a..f17525a963d1 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -175,4 +175,11 @@ static inline __u32 ror32(__u32 word, unsigned int shift) return (word >> shift) | (word << (32 - shift)); } +static inline unsigned fls_long(unsigned long l) +{ + if (sizeof(l) == 4) + return fls(l); + return fls64(l); +} + #endif diff --git a/include/linux/kernel.h b/include/linux/kernel.h index bb6e7ddee2fd..03d6cfaa5b8a 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -154,9 +154,10 @@ static inline int __attribute_pure__ long_log2(unsigned long x) return r; } -static inline unsigned long __attribute_const__ roundup_pow_of_two(unsigned long x) +static inline unsigned long +__attribute_const__ roundup_pow_of_two(unsigned long x) { - return (1UL << fls(x - 1)); + return 1UL << fls_long(x - 1); } extern int printk_ratelimit(void); -- cgit v1.2.3 From daff89f324755f87a060d5125a205c0755811ea9 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 25 Mar 2006 03:08:05 -0800 Subject: [PATCH] radix-tree documentation cleanups Documentation changes to help radix tree users avoid overrunning the tags array. RADIX_TREE_TAGS moves to linux/radix-tree.h and is now known as RADIX_TREE_MAX_TAGS (Nick Piggin's idea). Tag parameters are changed to unsigned, and some comments are updated. Signed-off-by: Jonathan Corbet Cc: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/radix-tree.h | 13 +++++++----- lib/radix-tree.c | 49 +++++++++++++++++++++++++--------------------- 2 files changed, 35 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index c57ff2fcb30a..dd83cca28001 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -45,6 +45,8 @@ do { \ (root)->rnode = NULL; \ } while (0) +#define RADIX_TREE_MAX_TAGS 2 + int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); void *radix_tree_lookup(struct radix_tree_root *, unsigned long); void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); @@ -55,15 +57,16 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, int radix_tree_preload(gfp_t gfp_mask); void radix_tree_init(void); void *radix_tree_tag_set(struct radix_tree_root *root, - unsigned long index, int tag); + unsigned long index, unsigned int tag); void *radix_tree_tag_clear(struct radix_tree_root *root, - unsigned long index, int tag); + unsigned long index, unsigned int tag); int radix_tree_tag_get(struct radix_tree_root *root, - unsigned long index, int tag); + unsigned long index, unsigned int tag); unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, - unsigned long first_index, unsigned int max_items, int tag); -int radix_tree_tagged(struct radix_tree_root *root, int tag); + unsigned long first_index, unsigned int max_items, + unsigned int tag); +int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag); static inline void radix_tree_preload_end(void) { diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 1e5b17dc7e3d..7097bb239e40 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -37,7 +37,6 @@ #else #define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */ #endif -#define RADIX_TREE_TAGS 2 #define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) #define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) @@ -48,7 +47,7 @@ struct radix_tree_node { unsigned int count; void *slots[RADIX_TREE_MAP_SIZE]; - unsigned long tags[RADIX_TREE_TAGS][RADIX_TREE_TAG_LONGS]; + unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS]; }; struct radix_tree_path { @@ -135,17 +134,20 @@ out: return ret; } -static inline void tag_set(struct radix_tree_node *node, int tag, int offset) +static inline void tag_set(struct radix_tree_node *node, unsigned int tag, + int offset) { __set_bit(offset, node->tags[tag]); } -static inline void tag_clear(struct radix_tree_node *node, int tag, int offset) +static inline void tag_clear(struct radix_tree_node *node, unsigned int tag, + int offset) { __clear_bit(offset, node->tags[tag]); } -static inline int tag_get(struct radix_tree_node *node, int tag, int offset) +static inline int tag_get(struct radix_tree_node *node, unsigned int tag, + int offset) { return test_bit(offset, node->tags[tag]); } @@ -154,7 +156,7 @@ static inline int tag_get(struct radix_tree_node *node, int tag, int offset) * Returns 1 if any slot in the node has this tag set. * Otherwise returns 0. */ -static inline int any_tag_set(struct radix_tree_node *node, int tag) +static inline int any_tag_set(struct radix_tree_node *node, unsigned int tag) { int idx; for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) { @@ -180,7 +182,7 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) { struct radix_tree_node *node; unsigned int height; - char tags[RADIX_TREE_TAGS]; + char tags[RADIX_TREE_MAX_TAGS]; int tag; /* Figure out what the height should be. */ @@ -197,7 +199,7 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) * Prepare the tag status of the top-level node for propagation * into the newly-pushed top-level node(s) */ - for (tag = 0; tag < RADIX_TREE_TAGS; tag++) { + for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { tags[tag] = 0; if (any_tag_set(root->rnode, tag)) tags[tag] = 1; @@ -211,7 +213,7 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) node->slots[0] = root->rnode; /* Propagate the aggregated tag info into the new root */ - for (tag = 0; tag < RADIX_TREE_TAGS; tag++) { + for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { if (tags[tag]) tag_set(node, tag, 0); } @@ -349,14 +351,15 @@ EXPORT_SYMBOL(radix_tree_lookup); * @index: index key * @tag: tag index * - * Set the search tag corresponging to @index in the radix tree. From + * Set the search tag (which must be < RADIX_TREE_MAX_TAGS) + * corresponding to @index in the radix tree. From * the root all the way down to the leaf node. * * Returns the address of the tagged item. Setting a tag on a not-present * item is a bug. */ void *radix_tree_tag_set(struct radix_tree_root *root, - unsigned long index, int tag) + unsigned long index, unsigned int tag) { unsigned int height, shift; struct radix_tree_node *slot; @@ -390,7 +393,8 @@ EXPORT_SYMBOL(radix_tree_tag_set); * @index: index key * @tag: tag index * - * Clear the search tag corresponging to @index in the radix tree. If + * Clear the search tag (which must be < RADIX_TREE_MAX_TAGS) + * corresponding to @index in the radix tree. If * this causes the leaf node to have no tags set then clear the tag in the * next-to-leaf node, etc. * @@ -398,7 +402,7 @@ EXPORT_SYMBOL(radix_tree_tag_set); * has the same return value and semantics as radix_tree_lookup(). */ void *radix_tree_tag_clear(struct radix_tree_root *root, - unsigned long index, int tag) + unsigned long index, unsigned int tag) { struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; struct radix_tree_node *slot; @@ -450,7 +454,7 @@ EXPORT_SYMBOL(radix_tree_tag_clear); * radix_tree_tag_get - get a tag on a radix tree node * @root: radix tree root * @index: index key - * @tag: tag index + * @tag: tag index (< RADIX_TREE_MAX_TAGS) * * Return values: * @@ -459,7 +463,7 @@ EXPORT_SYMBOL(radix_tree_tag_clear); * -1: tag present, unset */ int radix_tree_tag_get(struct radix_tree_root *root, - unsigned long index, int tag) + unsigned long index, unsigned int tag) { unsigned int height, shift; struct radix_tree_node *slot; @@ -592,7 +596,7 @@ EXPORT_SYMBOL(radix_tree_gang_lookup); */ static unsigned int __lookup_tag(struct radix_tree_root *root, void **results, unsigned long index, - unsigned int max_items, unsigned long *next_index, int tag) + unsigned int max_items, unsigned long *next_index, unsigned int tag) { unsigned int nr_found = 0; unsigned int shift; @@ -646,7 +650,7 @@ out: * @results: where the results of the lookup are placed * @first_index: start the lookup from this key * @max_items: place up to this many items at *results - * @tag: the tag index + * @tag: the tag index (< RADIX_TREE_MAX_TAGS) * * Performs an index-ascending scan of the tree for present items which * have the tag indexed by @tag set. Places the items at *@results and @@ -654,7 +658,8 @@ out: */ unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, - unsigned long first_index, unsigned int max_items, int tag) + unsigned long first_index, unsigned int max_items, + unsigned int tag) { const unsigned long max_index = radix_tree_maxindex(root->height); unsigned long cur_index = first_index; @@ -716,7 +721,7 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) struct radix_tree_node *slot; unsigned int height, shift; void *ret = NULL; - char tags[RADIX_TREE_TAGS]; + char tags[RADIX_TREE_MAX_TAGS]; int nr_cleared_tags; int tag; int offset; @@ -751,7 +756,7 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) * Clear all tags associated with the just-deleted item */ nr_cleared_tags = 0; - for (tag = 0; tag < RADIX_TREE_TAGS; tag++) { + for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { tags[tag] = 1; if (tag_get(pathp->node, tag, pathp->offset)) { tag_clear(pathp->node, tag, pathp->offset); @@ -763,7 +768,7 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) } for (pathp--; nr_cleared_tags && pathp->node; pathp--) { - for (tag = 0; tag < RADIX_TREE_TAGS; tag++) { + for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { if (tags[tag]) continue; @@ -801,7 +806,7 @@ EXPORT_SYMBOL(radix_tree_delete); * @root: radix tree root * @tag: tag to test */ -int radix_tree_tagged(struct radix_tree_root *root, int tag) +int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag) { struct radix_tree_node *rnode; rnode = root->rnode; -- cgit v1.2.3 From ccb46000f4bb459777686611157ac0eac928704e Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 25 Mar 2006 03:08:08 -0800 Subject: [PATCH] cpumask: uninline first_cpu() text data bss dec hex filename before: 3490577 1322408 360000 5172985 4eeef9 vmlinux after: 3488027 1322496 360128 5170651 4ee5db vmlinux Cc: Paul Jackson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cpumask.h | 11 ++++++----- lib/Makefile | 2 ++ lib/cpumask.c | 11 +++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 lib/cpumask.c (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 60e56c6e03dd..9b702fd24a72 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -212,11 +212,12 @@ static inline void __cpus_shift_left(cpumask_t *dstp, bitmap_shift_left(dstp->bits, srcp->bits, n, nbits); } -#define first_cpu(src) __first_cpu(&(src), NR_CPUS) -static inline int __first_cpu(const cpumask_t *srcp, int nbits) -{ - return min_t(int, nbits, find_first_bit(srcp->bits, nbits)); -} +#ifdef CONFIG_SMP +int __first_cpu(const cpumask_t *srcp); +#define first_cpu(src) __first_cpu(&(src)) +#else +#define first_cpu(src) 0 +#endif #define next_cpu(n, src) __next_cpu((n), &(src), NR_CPUS) static inline int __next_cpu(int n, const cpumask_t *srcp, int nbits) diff --git a/lib/Makefile b/lib/Makefile index 648b2c1242fd..f827e3c24ec0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -7,6 +7,8 @@ lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \ idr.o div64.o int_sqrt.o bitmap.o extable.o prio_tree.o \ sha1.o +lib-$(CONFIG_SMP) += cpumask.o + lib-y += kobject.o kref.o kobject_uevent.o klist.o obj-y += sort.o parser.o halfmd4.o iomap_copy.o diff --git a/lib/cpumask.c b/lib/cpumask.c new file mode 100644 index 000000000000..1560d97390dd --- /dev/null +++ b/lib/cpumask.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include + +int __first_cpu(const cpumask_t *srcp) +{ + return min_t(int, NR_CPUS, find_first_bit(srcp->bits, NR_CPUS)); +} +EXPORT_SYMBOL(__first_cpu); + -- cgit v1.2.3 From 3d18bd74a22d0bed3bc81fc64c4ba6344a10f155 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 25 Mar 2006 03:08:09 -0800 Subject: [PATCH] cpumask: uninline next_cpu() text data bss dec hex filename before: 3488027 1322496 360128 5170651 4ee5db vmlinux after: 3485112 1322480 359968 5167560 4ed9c8 vmlinux 2931 bytes saved Cc: Paul Jackson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cpumask.h | 11 ++++------- lib/cpumask.c | 5 +++++ 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 9b702fd24a72..4b29e508a0b6 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -215,16 +215,13 @@ static inline void __cpus_shift_left(cpumask_t *dstp, #ifdef CONFIG_SMP int __first_cpu(const cpumask_t *srcp); #define first_cpu(src) __first_cpu(&(src)) +int __next_cpu(int n, const cpumask_t *srcp); +#define next_cpu(n, src) __next_cpu((n), &(src)) #else -#define first_cpu(src) 0 +#define first_cpu(src) 0 +#define next_cpu(n, src) 1 #endif -#define next_cpu(n, src) __next_cpu((n), &(src), NR_CPUS) -static inline int __next_cpu(int n, const cpumask_t *srcp, int nbits) -{ - return min_t(int, nbits, find_next_bit(srcp->bits, nbits, n+1)); -} - #define cpumask_of_cpu(cpu) \ ({ \ typeof(_unused_cpumask_arg_) m; \ diff --git a/lib/cpumask.c b/lib/cpumask.c index 1560d97390dd..ba2f8543052c 100644 --- a/lib/cpumask.c +++ b/lib/cpumask.c @@ -9,3 +9,8 @@ int __first_cpu(const cpumask_t *srcp) } EXPORT_SYMBOL(__first_cpu); +int __next_cpu(int n, const cpumask_t *srcp) +{ + return min_t(int, NR_CPUS, find_next_bit(srcp->bits, NR_CPUS, n+1)); +} +EXPORT_SYMBOL(__next_cpu); -- cgit v1.2.3 From 8630282070b4a52b12cfa514ba8558e2f3d56360 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 25 Mar 2006 03:08:09 -0800 Subject: [PATCH] cpumask: uninline highest_possible_processor_id() Shrinks the only caller (net/bridge/netfilter/ebtables.c) by 174 bytes. Also, optimise highest_possible_processor_id() out of existence on CONFIG_SMP=n. Cc: Paul Jackson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cpumask.h | 15 ++++++--------- lib/cpumask.c | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 4b29e508a0b6..f770039344c5 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -396,6 +396,12 @@ extern cpumask_t cpu_present_map; #define cpu_present(cpu) ((cpu) == 0) #endif +#ifdef CONFIG_SMP +int highest_possible_processor_id(void); +#else +#define highest_possible_processor_id() 0 +#endif + #define any_online_cpu(mask) \ ({ \ int cpu; \ @@ -409,14 +415,5 @@ extern cpumask_t cpu_present_map; #define for_each_online_cpu(cpu) for_each_cpu_mask((cpu), cpu_online_map) #define for_each_present_cpu(cpu) for_each_cpu_mask((cpu), cpu_present_map) -/* Find the highest possible smp_processor_id() */ -#define highest_possible_processor_id() \ -({ \ - unsigned int cpu, highest = 0; \ - for_each_cpu_mask(cpu, cpu_possible_map) \ - highest = cpu; \ - highest; \ -}) - #endif /* __LINUX_CPUMASK_H */ diff --git a/lib/cpumask.c b/lib/cpumask.c index ba2f8543052c..ea25a034276c 100644 --- a/lib/cpumask.c +++ b/lib/cpumask.c @@ -14,3 +14,20 @@ int __next_cpu(int n, const cpumask_t *srcp) return min_t(int, NR_CPUS, find_next_bit(srcp->bits, NR_CPUS, n+1)); } EXPORT_SYMBOL(__next_cpu); + +/* + * Find the highest possible smp_processor_id() + * + * Note: if we're prepared to assume that cpu_possible_map never changes + * (reasonable) then this function should cache its return value. + */ +int highest_possible_processor_id(void) +{ + unsigned int cpu; + unsigned highest = 0; + + for_each_cpu_mask(cpu, cpu_possible_map) + highest = cpu; + return highest; +} +EXPORT_SYMBOL(highest_possible_processor_id); -- cgit v1.2.3 From 96a9b4d31eba4722ba7aad2cc15118a7799f499f Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 25 Mar 2006 03:08:10 -0800 Subject: [PATCH] cpumask: uninline any_online_cpu() text data bss dec hex filename before: 3605597 1363528 363328 5332453 515de5 vmlinux after: 3605295 1363612 363200 5332107 515c8b vmlinux 218 bytes saved. Also, optimise any_online_cpu() out of existence on CONFIG_SMP=n. This function seems inefficient. Can't we simply AND the two masks, then use find_first_bit()? Cc: Paul Jackson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cpumask.h | 13 +++---------- lib/cpumask.c | 12 ++++++++++++ 2 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index f770039344c5..99e6115d8e52 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -398,22 +398,15 @@ extern cpumask_t cpu_present_map; #ifdef CONFIG_SMP int highest_possible_processor_id(void); +#define any_online_cpu(mask) __any_online_cpu(&(mask)) +int __any_online_cpu(const cpumask_t *mask); #else #define highest_possible_processor_id() 0 +#define any_online_cpu(mask) 0 #endif -#define any_online_cpu(mask) \ -({ \ - int cpu; \ - for_each_cpu_mask(cpu, (mask)) \ - if (cpu_online(cpu)) \ - break; \ - cpu; \ -}) - #define for_each_cpu(cpu) for_each_cpu_mask((cpu), cpu_possible_map) #define for_each_online_cpu(cpu) for_each_cpu_mask((cpu), cpu_online_map) #define for_each_present_cpu(cpu) for_each_cpu_mask((cpu), cpu_present_map) - #endif /* __LINUX_CPUMASK_H */ diff --git a/lib/cpumask.c b/lib/cpumask.c index ea25a034276c..3a67dc5ada7d 100644 --- a/lib/cpumask.c +++ b/lib/cpumask.c @@ -31,3 +31,15 @@ int highest_possible_processor_id(void) return highest; } EXPORT_SYMBOL(highest_possible_processor_id); + +int __any_online_cpu(const cpumask_t *mask) +{ + int cpu; + + for_each_cpu_mask(cpu, *mask) { + if (cpu_online(cpu)) + break; + } + return cpu; +} +EXPORT_SYMBOL(__any_online_cpu); -- cgit v1.2.3 From 34f361ade2fb4a869f6a7714d01c04ce4cfa75d9 Mon Sep 17 00:00:00 2001 From: Ashok Raj Date: Sat, 25 Mar 2006 03:08:18 -0800 Subject: [PATCH] Check if cpu can be onlined before calling smp_prepare_cpu() - Moved check for online cpu out of smp_prepare_cpu() - Moved default declaration of smp_prepare_cpu() to kernel/cpu.c - Removed lock_cpu_hotplug() from smp_prepare_cpu() to around it, since its called from cpu_up() as well now. - Removed clearing from cpu_present_map during cpu_offline as it breaks using cpu_up() directly during a subsequent online operation. Signed-off-by: Ashok Raj Cc: Srivatsa Vaddagiri Cc: "Li, Shaohua" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/kernel/smpboot.c | 33 ++++++++++++++++++--------------- drivers/base/cpu.c | 9 +-------- include/linux/cpu.h | 1 - kernel/power/smp.c | 4 +--- 4 files changed, 20 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 4c470e99a742..82371d83bfa9 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -1003,7 +1003,6 @@ void cpu_exit_clear(void) cpu_clear(cpu, cpu_callout_map); cpu_clear(cpu, cpu_callin_map); - cpu_clear(cpu, cpu_present_map); cpu_clear(cpu, smp_commenced_mask); unmap_cpu_to_logical_apicid(cpu); @@ -1015,31 +1014,20 @@ struct warm_boot_cpu_info { int cpu; }; -static void __devinit do_warm_boot_cpu(void *p) +static void __cpuinit do_warm_boot_cpu(void *p) { struct warm_boot_cpu_info *info = p; do_boot_cpu(info->apicid, info->cpu); complete(info->complete); } -int __devinit smp_prepare_cpu(int cpu) +static int __cpuinit __smp_prepare_cpu(int cpu) { DECLARE_COMPLETION(done); struct warm_boot_cpu_info info; struct work_struct task; int apicid, ret; - lock_cpu_hotplug(); - - /* - * On x86, CPU0 is never offlined. Trying to bring up an - * already-booted CPU will hang. So check for that case. - */ - if (cpu_online(cpu)) { - ret = -EINVAL; - goto exit; - } - apicid = x86_cpu_to_apicid[cpu]; if (apicid == BAD_APICID) { ret = -ENODEV; @@ -1064,7 +1052,6 @@ int __devinit smp_prepare_cpu(int cpu) zap_low_mappings(); ret = 0; exit: - unlock_cpu_hotplug(); return ret; } #endif @@ -1392,6 +1379,22 @@ void __cpu_die(unsigned int cpu) int __devinit __cpu_up(unsigned int cpu) { +#ifdef CONFIG_HOTPLUG_CPU + int ret=0; + + /* + * We do warm boot only on cpus that had booted earlier + * Otherwise cold boot is all handled from smp_boot_cpus(). + * cpu_callin_map is set during AP kickstart process. Its reset + * when a cpu is taken offline from cpu_exit_clear(). + */ + if (!cpu_isset(cpu, cpu_callin_map)) + ret = __smp_prepare_cpu(cpu); + + if (ret) + return -EIO; +#endif + /* In case one didn't come up */ if (!cpu_isset(cpu, cpu_callin_map)) { printk(KERN_DEBUG "skipping cpu%d, didn't come online\n", cpu); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 29f3d7504da1..dd712b24ec91 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -19,11 +19,6 @@ EXPORT_SYMBOL(cpu_sysdev_class); static struct sys_device *cpu_sys_devices[NR_CPUS]; #ifdef CONFIG_HOTPLUG_CPU -int __attribute__((weak)) smp_prepare_cpu (int cpu) -{ - return 0; -} - static ssize_t show_online(struct sys_device *dev, char *buf) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); @@ -44,9 +39,7 @@ static ssize_t store_online(struct sys_device *dev, const char *buf, kobject_uevent(&dev->kobj, KOBJ_OFFLINE); break; case '1': - ret = smp_prepare_cpu(cpu->sysdev.id); - if (!ret) - ret = cpu_up(cpu->sysdev.id); + ret = cpu_up(cpu->sysdev.id); if (!ret) kobject_uevent(&dev->kobj, KOBJ_ONLINE); break; diff --git a/include/linux/cpu.h b/include/linux/cpu.h index d612b89dce33..08d50c53aab4 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -74,7 +74,6 @@ extern int lock_cpu_hotplug_interruptible(void); register_cpu_notifier(&fn##_nb); \ } int cpu_down(unsigned int cpu); -extern int __attribute__((weak)) smp_prepare_cpu(int cpu); #define cpu_is_offline(cpu) unlikely(!cpu_online(cpu)) #else #define lock_cpu_hotplug() do { } while (0) diff --git a/kernel/power/smp.c b/kernel/power/smp.c index 911fc62b8225..5957312b2d68 100644 --- a/kernel/power/smp.c +++ b/kernel/power/smp.c @@ -49,9 +49,7 @@ void enable_nonboot_cpus(void) printk("Thawing cpus ...\n"); for_each_cpu_mask(cpu, frozen_cpus) { - error = smp_prepare_cpu(cpu); - if (!error) - error = cpu_up(cpu); + error = cpu_up(cpu); if (!error) { printk("CPU%d is up\n", cpu); continue; -- cgit v1.2.3 From d12ddde2bbf46b34eae3fb3fd36c0e42832b537c Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sat, 25 Mar 2006 03:08:21 -0800 Subject: [PATCH] udf: remove duplicate definitions This patch removes duplicate definitions from include/linux/udf_fs_i.h which are already defined in fs/udf/ecma_167.h. Signed-off-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/udf_fs_i.h | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'include') diff --git a/include/linux/udf_fs_i.h b/include/linux/udf_fs_i.h index 1e7508420fcf..ffaf05679ffb 100644 --- a/include/linux/udf_fs_i.h +++ b/include/linux/udf_fs_i.h @@ -15,27 +15,6 @@ #ifdef __KERNEL__ -#ifndef _ECMA_167_H -typedef struct -{ - __u32 logicalBlockNum; - __u16 partitionReferenceNum; -} __attribute__ ((packed)) lb_addr; - -typedef struct -{ - __u32 extLength; - __u32 extPosition; -} __attribute__ ((packed)) short_ad; - -typedef struct -{ - __u32 extLength; - lb_addr extLocation; - __u8 impUse[6]; -} __attribute__ ((packed)) long_ad; -#endif - struct udf_inode_info { struct timespec i_crtime; -- cgit v1.2.3 From 5ddcfa878d5b10b0ab94251a4229a8a9daaf93ed Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Sat, 25 Mar 2006 03:08:28 -0800 Subject: [PATCH] remove pps support This removes the support for pps. It's completely unused within the kernel and is basically in the way for further cleanups. It should be easier to readd proper support for it after the rest has been converted to NTP4 (where the pps mechanisms are quite different from NTP3 anyway). Signed-off-by: Roman Zippel Cc: Adrian Bunk Cc: john stultz Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/timex.h | 41 ----------------------------------- kernel/time.c | 59 ++++++++++++++------------------------------------- kernel/timer.c | 13 ++---------- 3 files changed, 18 insertions(+), 95 deletions(-) (limited to 'include') diff --git a/include/linux/timex.h b/include/linux/timex.h index b7ca1204e42a..82dc9ae79d37 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -97,37 +97,10 @@ #define MAXPHASE 512000L /* max phase error (us) */ #define MAXFREQ (512L << SHIFT_USEC) /* max frequency error (ppm) */ -#define MAXTIME (200L << PPS_AVG) /* max PPS error (jitter) (200 us) */ #define MINSEC 16L /* min interval between updates (s) */ #define MAXSEC 1200L /* max interval between updates (s) */ #define NTP_PHASE_LIMIT (MAXPHASE << 5) /* beyond max. dispersion */ -/* - * The following defines are used only if a pulse-per-second (PPS) - * signal is available and connected via a modem control lead, such as - * produced by the optional ppsclock feature incorporated in the Sun - * asynch driver. They establish the design parameters of the frequency- - * lock loop used to discipline the CPU clock oscillator to the PPS - * signal. - * - * PPS_AVG is the averaging factor for the frequency loop, as well as - * the time and frequency dispersion. - * - * PPS_SHIFT and PPS_SHIFTMAX specify the minimum and maximum - * calibration intervals, respectively, in seconds as a power of two. - * - * PPS_VALID is the maximum interval before the PPS signal is considered - * invalid and protocol updates used directly instead. - * - * MAXGLITCH is the maximum interval before a time offset of more than - * MAXTIME is believed. - */ -#define PPS_AVG 2 /* pps averaging constant (shift) */ -#define PPS_SHIFT 2 /* min interval duration (s) (shift) */ -#define PPS_SHIFTMAX 8 /* max interval duration (s) (shift) */ -#define PPS_VALID 120 /* pps signal watchdog max (s) */ -#define MAXGLITCH 30 /* pps signal glitch max (s) */ - /* * syscall interface - used (mainly by NTP daemon) * to discipline kernel clock oscillator @@ -246,20 +219,6 @@ extern long time_reftime; /* time at last adjustment (s) */ extern long time_adjust; /* The amount of adjtime left */ extern long time_next_adjust; /* Value for time_adjust at next tick */ -/* interface variables pps->timer interrupt */ -extern long pps_offset; /* pps time offset (us) */ -extern long pps_jitter; /* time dispersion (jitter) (us) */ -extern long pps_freq; /* frequency offset (scaled ppm) */ -extern long pps_stabil; /* frequency dispersion (scaled ppm) */ -extern long pps_valid; /* pps signal watchdog counter */ - -/* interface variables pps->adjtimex */ -extern int pps_shift; /* interval duration (s) (shift) */ -extern long pps_jitcnt; /* jitter limit exceeded */ -extern long pps_calcnt; /* calibration intervals */ -extern long pps_errcnt; /* calibration errors */ -extern long pps_stbcnt; /* stability limit exceeded */ - /** * ntp_clear - Clears the NTP state variables * diff --git a/kernel/time.c b/kernel/time.c index 804539165d8b..e00a97b77241 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -202,24 +202,6 @@ asmlinkage long sys_settimeofday(struct timeval __user *tv, return do_sys_settimeofday(tv ? &new_ts : NULL, tz ? &new_tz : NULL); } -long pps_offset; /* pps time offset (us) */ -long pps_jitter = MAXTIME; /* time dispersion (jitter) (us) */ - -long pps_freq; /* frequency offset (scaled ppm) */ -long pps_stabil = MAXFREQ; /* frequency dispersion (scaled ppm) */ - -long pps_valid = PPS_VALID; /* pps signal watchdog counter */ - -int pps_shift = PPS_SHIFT; /* interval duration (s) (shift) */ - -long pps_jitcnt; /* jitter limit exceeded */ -long pps_calcnt; /* calibration intervals */ -long pps_errcnt; /* calibration errors */ -long pps_stbcnt; /* stability limit exceeded */ - -/* hook for a loadable hardpps kernel module */ -void (*hardpps_ptr)(struct timeval *); - /* we call this to notify the arch when the clock is being * controlled. If no such arch routine, do nothing. */ @@ -279,7 +261,7 @@ int do_adjtimex(struct timex *txc) result = -EINVAL; goto leave; } - time_freq = txc->freq - pps_freq; + time_freq = txc->freq; } if (txc->modes & ADJ_MAXERROR) { @@ -312,10 +294,8 @@ int do_adjtimex(struct timex *txc) if ((time_next_adjust = txc->offset) == 0) time_adjust = 0; } - else if ( time_status & (STA_PLL | STA_PPSTIME) ) { - ltemp = (time_status & (STA_PPSTIME | STA_PPSSIGNAL)) == - (STA_PPSTIME | STA_PPSSIGNAL) ? - pps_offset : txc->offset; + else if (time_status & STA_PLL) { + ltemp = txc->offset; /* * Scale the phase adjustment and @@ -356,23 +336,14 @@ int do_adjtimex(struct timex *txc) } time_freq = min(time_freq, time_tolerance); time_freq = max(time_freq, -time_tolerance); - } /* STA_PLL || STA_PPSTIME */ + } /* STA_PLL */ } /* txc->modes & ADJ_OFFSET */ if (txc->modes & ADJ_TICK) { tick_usec = txc->tick; tick_nsec = TICK_USEC_TO_NSEC(tick_usec); } } /* txc->modes */ -leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0 - || ((time_status & (STA_PPSFREQ|STA_PPSTIME)) != 0 - && (time_status & STA_PPSSIGNAL) == 0) - /* p. 24, (b) */ - || ((time_status & (STA_PPSTIME|STA_PPSJITTER)) - == (STA_PPSTIME|STA_PPSJITTER)) - /* p. 24, (c) */ - || ((time_status & STA_PPSFREQ) != 0 - && (time_status & (STA_PPSWANDER|STA_PPSERROR)) != 0)) - /* p. 24, (d) */ +leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0) result = TIME_ERROR; if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) @@ -380,7 +351,7 @@ leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0 else { txc->offset = shift_right(time_offset, SHIFT_UPDATE); } - txc->freq = time_freq + pps_freq; + txc->freq = time_freq; txc->maxerror = time_maxerror; txc->esterror = time_esterror; txc->status = time_status; @@ -388,14 +359,16 @@ leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0 txc->precision = time_precision; txc->tolerance = time_tolerance; txc->tick = tick_usec; - txc->ppsfreq = pps_freq; - txc->jitter = pps_jitter >> PPS_AVG; - txc->shift = pps_shift; - txc->stabil = pps_stabil; - txc->jitcnt = pps_jitcnt; - txc->calcnt = pps_calcnt; - txc->errcnt = pps_errcnt; - txc->stbcnt = pps_stbcnt; + + /* PPS is not implemented, so these are zero */ + txc->ppsfreq = 0; + txc->jitter = 0; + txc->shift = 0; + txc->stabil = 0; + txc->jitcnt = 0; + txc->calcnt = 0; + txc->errcnt = 0; + txc->stbcnt = 0; write_sequnlock_irq(&xtime_lock); do_gettimeofday(&txc->time); notify_arch_cmos_timer(); diff --git a/kernel/timer.c b/kernel/timer.c index 13fa72cac7d8..ab189dd187cb 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -697,18 +697,9 @@ static void second_overflow(void) /* * Compute the frequency estimate and additional phase adjustment due - * to frequency error for the next second. When the PPS signal is - * engaged, gnaw on the watchdog counter and update the frequency - * computed by the pll and the PPS signal. + * to frequency error for the next second. */ - pps_valid++; - if (pps_valid == PPS_VALID) { /* PPS signal lost */ - pps_jitter = MAXTIME; - pps_stabil = MAXFREQ; - time_status &= ~(STA_PPSSIGNAL | STA_PPSJITTER | - STA_PPSWANDER | STA_PPSERROR); - } - ltemp = time_freq + pps_freq; + ltemp = time_freq; time_adj += shift_right(ltemp,(SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE)); #if HZ == 100 -- cgit v1.2.3 From 913bd906019514579b3c7ec5ab9c463e89207a57 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 25 Mar 2006 16:29:09 +0100 Subject: [PATCH] x86_64: Increase the variability of the process stack on 64bit architectures 8MB is not really very random, use 1GB (or more with larger page sizes) instead. Also use the low bits of the random generator output now instead of throwing them away. Only enabled on x86-64 right now. Other architectures need to add a suitable STACK_RND_MASK Cc: mingo@elte.hu Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- fs/binfmt_elf.c | 13 +++++++++---- include/asm-x86_64/elf.h | 4 ++++ 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 4349113881fb..537893a16014 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -500,17 +500,22 @@ out: #define INTERPRETER_AOUT 1 #define INTERPRETER_ELF 2 +#ifndef STACK_RND_MASK +#define STACK_RND_MASK 0x7ff /* with 4K pages 8MB of VA */ +#endif static unsigned long randomize_stack_top(unsigned long stack_top) { unsigned int random_variable = 0; - if (current->flags & PF_RANDOMIZE) - random_variable = get_random_int() % (8*1024*1024); + if (current->flags & PF_RANDOMIZE) { + random_variable = get_random_int() & STACK_RND_MASK; + random_variable <<= PAGE_SHIFT; + } #ifdef CONFIG_STACK_GROWSUP - return PAGE_ALIGN(stack_top + random_variable); + return PAGE_ALIGN(stack_top) + random_variable; #else - return PAGE_ALIGN(stack_top - random_variable); + return PAGE_ALIGN(stack_top) - random_variable; #endif } diff --git a/include/asm-x86_64/elf.h b/include/asm-x86_64/elf.h index 43862cd6a569..c98633af07d2 100644 --- a/include/asm-x86_64/elf.h +++ b/include/asm-x86_64/elf.h @@ -8,6 +8,7 @@ #include #include #include +#include /* x86-64 relocation types */ #define R_X86_64_NONE 0 /* No reloc */ @@ -157,6 +158,9 @@ extern int dump_task_fpu (struct task_struct *, elf_fpregset_t *); #define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs) #define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs) +/* 1GB for 64bit, 8MB for 32bit */ +#define STACK_RND_MASK (is_compat_task() ? 0x7ff : 0x3fffff) + #endif #endif -- cgit v1.2.3 From abe059e7590fd4475285f2d037c70dec712a4572 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 25 Mar 2006 16:29:12 +0100 Subject: [PATCH] x86_64: Rename struct node in x86-64 NUMA code to struct bootnode It conflicts with the struct node in node.h Actually the x86-64 version was there first, but .. Suggested by Jan Beulich Cc: jbeulich@novell.com Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/x86_64/mm/k8topology.c | 2 +- arch/x86_64/mm/numa.c | 6 +++--- arch/x86_64/mm/srat.c | 8 ++++---- include/asm-x86_64/numa.h | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/x86_64/mm/k8topology.c b/arch/x86_64/mm/k8topology.c index dd60e71fdba6..7c45c2d2b8b2 100644 --- a/arch/x86_64/mm/k8topology.c +++ b/arch/x86_64/mm/k8topology.c @@ -43,7 +43,7 @@ static __init int find_northbridge(void) int __init k8_scan_nodes(unsigned long start, unsigned long end) { unsigned long prevbase; - struct node nodes[8]; + struct bootnode nodes[8]; int nodeid, i, nb; unsigned char nodeids[8]; int found = 0; diff --git a/arch/x86_64/mm/numa.c b/arch/x86_64/mm/numa.c index 22e51beee8d3..e4b62753a19a 100644 --- a/arch/x86_64/mm/numa.c +++ b/arch/x86_64/mm/numa.c @@ -47,7 +47,7 @@ int numa_off __initdata; * -1 if node overlap or lost ram (shift too big) */ static int __init -populate_memnodemap(const struct node *nodes, int numnodes, int shift) +populate_memnodemap(const struct bootnode *nodes, int numnodes, int shift) { int i; int res = -1; @@ -74,7 +74,7 @@ populate_memnodemap(const struct node *nodes, int numnodes, int shift) return res; } -int __init compute_hash_shift(struct node *nodes, int numnodes) +int __init compute_hash_shift(struct bootnode *nodes, int numnodes) { int shift = 20; @@ -191,7 +191,7 @@ int numa_fake __initdata = 0; static int numa_emulation(unsigned long start_pfn, unsigned long end_pfn) { int i; - struct node nodes[MAX_NUMNODES]; + struct bootnode nodes[MAX_NUMNODES]; unsigned long sz = ((end_pfn - start_pfn)<start == nd->end) continue; if (nd->end > start && nd->start < end) @@ -70,7 +70,7 @@ static __init int conflicting_nodes(unsigned long start, unsigned long end) static __init void cutoff_node(int i, unsigned long start, unsigned long end) { - struct node *nd = &nodes[i]; + struct bootnode *nd = &nodes[i]; if (nd->start < start) { nd->start = start; if (nd->end < nd->start) @@ -159,7 +159,7 @@ acpi_numa_processor_affinity_init(struct acpi_table_processor_affinity *pa) void __init acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma) { - struct node *nd; + struct bootnode *nd; unsigned long start, end; int node, pxm; int i; diff --git a/include/asm-x86_64/numa.h b/include/asm-x86_64/numa.h index dffe276ca2df..f6cbb4cbb5a3 100644 --- a/include/asm-x86_64/numa.h +++ b/include/asm-x86_64/numa.h @@ -4,11 +4,11 @@ #include #include -struct node { +struct bootnode { u64 start,end; }; -extern int compute_hash_shift(struct node *nodes, int numnodes); +extern int compute_hash_shift(struct bootnode *nodes, int numnodes); extern int pxm_to_node(int nid); #define ZONE_ALIGN (1UL << (MAX_ORDER+PAGE_SHIFT)) -- cgit v1.2.3 From 2b514e74f4e59e3b8e54891580fef2c9ff6c7bd0 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Sat, 25 Mar 2006 16:29:22 +0100 Subject: [PATCH] x86_64: eliminate set_debug() For consistency and to have only a single place of definition, replace set_debug() uses with set_debugreg(), and eliminate the definition of thj former. Signed-off-by: Jan Beulich Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/x86_64/kernel/process.c | 2 +- arch/x86_64/kernel/setup64.c | 12 ++++++------ include/asm-x86_64/suspend.h | 4 +--- include/asm-x86_64/system.h | 6 ------ 4 files changed, 8 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c index 80a8f3079178..0cb93abbf564 100644 --- a/arch/x86_64/kernel/process.c +++ b/arch/x86_64/kernel/process.c @@ -508,7 +508,7 @@ out: /* * This special macro can be used to load a debugging register */ -#define loaddebug(thread,r) set_debug(thread->debugreg ## r, r) +#define loaddebug(thread,r) set_debugreg(thread->debugreg ## r, r) /* * switch_to(x,y) should switch tasks from x to y. diff --git a/arch/x86_64/kernel/setup64.c b/arch/x86_64/kernel/setup64.c index 349d55c2ac4e..044587812b05 100644 --- a/arch/x86_64/kernel/setup64.c +++ b/arch/x86_64/kernel/setup64.c @@ -281,12 +281,12 @@ void __cpuinit cpu_init (void) * Clear all 6 debug registers: */ - set_debug(0UL, 0); - set_debug(0UL, 1); - set_debug(0UL, 2); - set_debug(0UL, 3); - set_debug(0UL, 6); - set_debug(0UL, 7); + set_debugreg(0UL, 0); + set_debugreg(0UL, 1); + set_debugreg(0UL, 2); + set_debugreg(0UL, 3); + set_debugreg(0UL, 6); + set_debugreg(0UL, 7); fpu_init(); } diff --git a/include/asm-x86_64/suspend.h b/include/asm-x86_64/suspend.h index bb9f40597d09..bc7f81715e5e 100644 --- a/include/asm-x86_64/suspend.h +++ b/include/asm-x86_64/suspend.h @@ -39,9 +39,7 @@ extern unsigned long saved_context_r12, saved_context_r13, saved_context_r14, sa extern unsigned long saved_context_eflags; #define loaddebug(thread,register) \ - __asm__("movq %0,%%db" #register \ - : /* no output */ \ - :"r" ((thread)->debugreg##register)) + set_debugreg((thread)->debugreg##register, register) extern void fix_processor_context(void); diff --git a/include/asm-x86_64/system.h b/include/asm-x86_64/system.h index b7f66034ae7a..397598980228 100644 --- a/include/asm-x86_64/system.h +++ b/include/asm-x86_64/system.h @@ -70,12 +70,6 @@ extern void load_gs_index(unsigned); ".previous" \ : :"r" (value), "r" (0)) -#define set_debug(value,register) \ - __asm__("movq %0,%%db" #register \ - : /* no output */ \ - :"r" ((unsigned long) value)) - - #ifdef __KERNEL__ struct alt_instr { __u8 *instr; /* original instruction */ -- cgit v1.2.3 From 8c914cb704a11460eec7ed2a572bb5e9bd513d24 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Sat, 25 Mar 2006 16:29:40 +0100 Subject: [PATCH] x86_64: actively synchronize vmalloc area when registering certain callbacks While the modular aspect of the respective i386 patch doesn't apply to x86-64 (as the top level page directory entry is shared between modules and the base kernel), handlers registered with register_die_notifier() are still under similar constraints for touching ioremap()ed or vmalloc()ed memory. The likelihood of this problem becoming visible is of course significantly lower, as the assigned virtual addresses would have to cross a 2**39 byte boundary. This is because the callback gets invoked (a) in the page fault path before the top level page table propagation gets carried out (hence a fault to propagate the top level page table entry/entries mapping to module's code/data would nest infinitly) and (b) in the NMI path, where nested faults must absolutely not happen, since otherwise the IRET from the nested fault re-enables NMIs, potentially resulting in nested NMI occurences. Signed-off-by: Jan Beulich Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/x86_64/kernel/nmi.c | 1 + arch/x86_64/kernel/traps.c | 2 ++ arch/x86_64/mm/fault.c | 73 ++++++++++++++++++++++++++++++++++++-------- include/asm-x86_64/pgalloc.h | 28 +++++++++++++++++ include/asm-x86_64/pgtable.h | 4 +++ 5 files changed, 95 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c index 66c009e10bac..d9e4067faf05 100644 --- a/arch/x86_64/kernel/nmi.c +++ b/arch/x86_64/kernel/nmi.c @@ -534,6 +534,7 @@ asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code) void set_nmi_callback(nmi_callback_t callback) { + vmalloc_sync_all(); rcu_assign_pointer(nmi_callback, callback); } diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 28d50dc540e8..b25bc904d42d 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c @@ -78,6 +78,8 @@ int register_die_notifier(struct notifier_block *nb) { int err = 0; unsigned long flags; + + vmalloc_sync_all(); spin_lock_irqsave(&die_notifier_lock, flags); err = notifier_chain_register(&die_chain, nb); spin_unlock_irqrestore(&die_notifier_lock, flags); diff --git a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c index 2e7c3c8ffe03..de91e17daf6f 100644 --- a/arch/x86_64/mm/fault.c +++ b/arch/x86_64/mm/fault.c @@ -264,6 +264,8 @@ static int vmalloc_fault(unsigned long address) return -1; if (pgd_none(*pgd)) set_pgd(pgd, *pgd_ref); + else + BUG_ON(pgd_page(*pgd) != pgd_page(*pgd_ref)); /* Below here mismatches are bugs because these lower tables are shared */ @@ -314,16 +316,6 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, /* get the address */ __asm__("movq %%cr2,%0":"=r" (address)); - if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14, - SIGSEGV) == NOTIFY_STOP) - return; - - if (likely(regs->eflags & X86_EFLAGS_IF)) - local_irq_enable(); - - if (unlikely(page_fault_trace)) - printk("pagefault rip:%lx rsp:%lx cs:%lu ss:%lu address %lx error %lx\n", - regs->rip,regs->rsp,regs->cs,regs->ss,address,error_code); tsk = current; mm = tsk->mm; @@ -351,10 +343,12 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, */ if (!(error_code & (PF_RSVD|PF_USER|PF_PROT)) && ((address >= VMALLOC_START && address < VMALLOC_END))) { - if (vmalloc_fault(address) < 0) - goto bad_area_nosemaphore; - return; + if (vmalloc_fault(address) >= 0) + return; } + if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14, + SIGSEGV) == NOTIFY_STOP) + return; /* * Don't take the mm semaphore here. If we fixup a prefetch * fault we could otherwise deadlock. @@ -362,6 +356,17 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, goto bad_area_nosemaphore; } + if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14, + SIGSEGV) == NOTIFY_STOP) + return; + + if (likely(regs->eflags & X86_EFLAGS_IF)) + local_irq_enable(); + + if (unlikely(page_fault_trace)) + printk("pagefault rip:%lx rsp:%lx cs:%lu ss:%lu address %lx error %lx\n", + regs->rip,regs->rsp,regs->cs,regs->ss,address,error_code); + if (unlikely(error_code & PF_RSVD)) pgtable_bad(address, regs, error_code); @@ -571,6 +576,48 @@ do_sigbus: return; } +DEFINE_SPINLOCK(pgd_lock); +struct page *pgd_list; + +void vmalloc_sync_all(void) +{ + /* Note that races in the updates of insync and start aren't + problematic: + insync can only get set bits added, and updates to start are only + improving performance (without affecting correctness if undone). */ + static DECLARE_BITMAP(insync, PTRS_PER_PGD); + static unsigned long start = VMALLOC_START & PGDIR_MASK; + unsigned long address; + + for (address = start; address <= VMALLOC_END; address += PGDIR_SIZE) { + if (!test_bit(pgd_index(address), insync)) { + const pgd_t *pgd_ref = pgd_offset_k(address); + struct page *page; + + if (pgd_none(*pgd_ref)) + continue; + spin_lock(&pgd_lock); + for (page = pgd_list; page; + page = (struct page *)page->index) { + pgd_t *pgd; + pgd = (pgd_t *)page_address(page) + pgd_index(address); + if (pgd_none(*pgd)) + set_pgd(pgd, *pgd_ref); + else + BUG_ON(pgd_page(*pgd) != pgd_page(*pgd_ref)); + } + spin_unlock(&pgd_lock); + set_bit(pgd_index(address), insync); + } + if (address == start) + start = address + PGDIR_SIZE; + } + /* Check that there is no need to do the same for the modules area. */ + BUILD_BUG_ON(!(MODULES_VADDR > __START_KERNEL)); + BUILD_BUG_ON(!(((MODULES_END - 1) & PGDIR_MASK) == + (__START_KERNEL & PGDIR_MASK))); +} + static int __init enable_pagefaulttrace(char *str) { page_fault_trace = 1; diff --git a/include/asm-x86_64/pgalloc.h b/include/asm-x86_64/pgalloc.h index 08cad2482bcb..43d4c333a8b1 100644 --- a/include/asm-x86_64/pgalloc.h +++ b/include/asm-x86_64/pgalloc.h @@ -45,12 +45,39 @@ static inline void pud_free (pud_t *pud) free_page((unsigned long)pud); } +static inline void pgd_list_add(pgd_t *pgd) +{ + struct page *page = virt_to_page(pgd); + + spin_lock(&pgd_lock); + page->index = (pgoff_t)pgd_list; + if (pgd_list) + pgd_list->private = (unsigned long)&page->index; + pgd_list = page; + page->private = (unsigned long)&pgd_list; + spin_unlock(&pgd_lock); +} + +static inline void pgd_list_del(pgd_t *pgd) +{ + struct page *next, **pprev, *page = virt_to_page(pgd); + + spin_lock(&pgd_lock); + next = (struct page *)page->index; + pprev = (struct page **)page->private; + *pprev = next; + if (next) + next->private = (unsigned long)pprev; + spin_unlock(&pgd_lock); +} + static inline pgd_t *pgd_alloc(struct mm_struct *mm) { unsigned boundary; pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT); if (!pgd) return NULL; + pgd_list_add(pgd); /* * Copy kernel pointers in from init. * Could keep a freelist or slab cache of those because the kernel @@ -67,6 +94,7 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) static inline void pgd_free(pgd_t *pgd) { BUG_ON((unsigned long)pgd & (PAGE_SIZE-1)); + pgd_list_del(pgd); free_page((unsigned long)pgd); } diff --git a/include/asm-x86_64/pgtable.h b/include/asm-x86_64/pgtable.h index def903287193..31e83c3bd022 100644 --- a/include/asm-x86_64/pgtable.h +++ b/include/asm-x86_64/pgtable.h @@ -420,6 +420,10 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val }) +extern spinlock_t pgd_lock; +extern struct page *pgd_list; +void vmalloc_sync_all(void); + #endif /* !__ASSEMBLY__ */ extern int kern_addr_valid(unsigned long addr); -- cgit v1.2.3 From 86ebcea899ff01274c1e8e15bf1d1f1cf5fac471 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Sat, 25 Mar 2006 16:29:43 +0100 Subject: [PATCH] x86_64: remove dead do_softirq_thunk Appearantly a left-over... Signed-off-by: Jan Beulich Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/x86_64/kernel/x8664_ksyms.c | 3 --- arch/x86_64/lib/thunk.S | 1 - include/asm-x86_64/proto.h | 2 -- 3 files changed, 6 deletions(-) (limited to 'include') diff --git a/arch/x86_64/kernel/x8664_ksyms.c b/arch/x86_64/kernel/x8664_ksyms.c index c9dc7e46731e..365e5fbae9a0 100644 --- a/arch/x86_64/kernel/x8664_ksyms.c +++ b/arch/x86_64/kernel/x8664_ksyms.c @@ -151,9 +151,6 @@ EXPORT_SYMBOL(cpu_sibling_map); EXPORT_SYMBOL(smp_num_siblings); #endif -extern void do_softirq_thunk(void); -EXPORT_SYMBOL(do_softirq_thunk); - #ifdef CONFIG_BUG EXPORT_SYMBOL(out_of_line_bug); #endif diff --git a/arch/x86_64/lib/thunk.S b/arch/x86_64/lib/thunk.S index acc1e2ca7ed7..e49af0032e94 100644 --- a/arch/x86_64/lib/thunk.S +++ b/arch/x86_64/lib/thunk.S @@ -42,7 +42,6 @@ thunk rwsem_wake_thunk,rwsem_wake thunk rwsem_downgrade_thunk,rwsem_downgrade_wake #endif - thunk do_softirq_thunk,do_softirq thunk __down_failed,__down thunk_retrax __down_failed_interruptible,__down_interruptible diff --git a/include/asm-x86_64/proto.h b/include/asm-x86_64/proto.h index 3ba8fd45fcb3..977fe58e971c 100644 --- a/include/asm-x86_64/proto.h +++ b/include/asm-x86_64/proto.h @@ -53,8 +53,6 @@ extern int sysctl_vsyscall; extern int nohpet; extern unsigned long vxtime_hz; -extern void do_softirq_thunk(void); - extern int numa_setup(char *opt); extern int setup_early_printk(char *); -- cgit v1.2.3 From 6edfba1b33c701108717f4e036320fc39abe1912 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 25 Mar 2006 16:29:49 +0100 Subject: [PATCH] x86_64: Don't define string functions to builtin gcc should handle this anyways, and it causes problems when sprintf is turned into strcpy by gcc behind our backs and the C fallback version of strcpy is actually defining __builtin_strcpy Then drop -ffreestanding from the main Makefile because it isn't needed anymore and implies -fno-builtin, which is wrong now. (it was only added for x86-64, so dropping it should be safe) Noticed by Roman Zippel Cc: Roman Zippel Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- Makefile | 3 +-- arch/i386/Makefile | 3 +++ include/asm-x86_64/string.h | 17 +++-------------- 3 files changed, 7 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/Makefile b/Makefile index 7bc2f4ad8093..af6210d48836 100644 --- a/Makefile +++ b/Makefile @@ -306,8 +306,7 @@ LINUXINCLUDE := -Iinclude \ CPPFLAGS := -D__KERNEL__ $(LINUXINCLUDE) CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ - -fno-strict-aliasing -fno-common \ - -ffreestanding + -fno-strict-aliasing -fno-common AFLAGS := -D__ASSEMBLY__ # Read KERNELRELEASE from .kernelrelease (if it exists) diff --git a/arch/i386/Makefile b/arch/i386/Makefile index ff6973a85c8f..c848a5b30391 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -39,6 +39,9 @@ include $(srctree)/arch/i386/Makefile.cpu cflags-$(CONFIG_REGPARM) += -mregparm=3 +# temporary until string.h is fixed +cflags-y += -ffreestanding + # Disable unit-at-a-time mode on pre-gcc-4.0 compilers, it makes gcc use # a lot more stack due to the lack of sharing of stacklots: CFLAGS += $(shell if [ $(call cc-version) -lt 0400 ] ; then echo $(call cc-option,-fno-unit-at-a-time); fi ;) diff --git a/include/asm-x86_64/string.h b/include/asm-x86_64/string.h index a3493ee282bb..ee6bf275349e 100644 --- a/include/asm-x86_64/string.h +++ b/include/asm-x86_64/string.h @@ -40,26 +40,15 @@ extern void *__memcpy(void *to, const void *from, size_t len); #define __HAVE_ARCH_MEMSET -#define memset __builtin_memset +void *memset(void *s, int c, size_t n); #define __HAVE_ARCH_MEMMOVE void * memmove(void * dest,const void *src,size_t count); -/* Use C out of line version for memcmp */ -#define memcmp __builtin_memcmp int memcmp(const void * cs,const void * ct,size_t count); - -/* out of line string functions use always C versions */ -#define strlen __builtin_strlen size_t strlen(const char * s); - -#define strcpy __builtin_strcpy -char * strcpy(char * dest,const char *src); - -#define strcat __builtin_strcat -char * strcat(char * dest, const char * src); - -#define strcmp __builtin_strcmp +char *strcpy(char * dest,const char *src); +char *strcat(char * dest, const char * src); int strcmp(const char * cs,const char * ct); #endif /* __KERNEL__ */ -- cgit v1.2.3 From f083a329e63d471a5e9238e837772b1b76c218db Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 25 Mar 2006 16:30:19 +0100 Subject: [PATCH] x86_64: Clean up and tweak ACPI blacklist year code - Move the core parser into dmi_scan.c. It can be useful for other subsystems too. - Differentiate between field doesn't exist and field is 0 or unparseable. The first case is likely an old BIOS with broken ACPI, the later is likely a slightly buggy BIOS where someone forget to edit the date. Don't blacklist in the later case. Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/i386/kernel/dmi_scan.c | 30 ++++++++++++++++++++++++++++++ drivers/acpi/blacklist.c | 27 ++++++--------------------- include/linux/dmi.h | 2 ++ 3 files changed, 38 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c index ca2a0cbcac04..d2dfd9c8d691 100644 --- a/arch/i386/kernel/dmi_scan.c +++ b/arch/i386/kernel/dmi_scan.c @@ -299,3 +299,33 @@ struct dmi_device * dmi_find_device(int type, const char *name, return NULL; } EXPORT_SYMBOL(dmi_find_device); + +/** + * dmi_get_year - Return year of a DMI date + * @field: data index (like dmi_get_system_info) + * + * Returns -1 when the field doesn't exist. 0 when it is broken. + */ +int dmi_get_year(int field) +{ + int year; + char *s = dmi_get_system_info(field); + + if (!s) + return -1; + if (*s == '\0') + return 0; + s = strrchr(s, '/'); + if (!s) + return 0; + + s += 1; + year = simple_strtoul(s, NULL, 0); + if (year && year < 100) { /* 2-digit year */ + year += 1900; + if (year < 1996) /* no dates < spec 1.0 */ + year += 100; + } + + return year; +} diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 9824f679a910..f9c972b26f4f 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -77,28 +77,13 @@ static struct acpi_blacklist_item acpi_blacklist[] __initdata = { static int __init blacklist_by_year(void) { - int year; - char *s = dmi_get_system_info(DMI_BIOS_DATE); - - if (!s) - return 0; - if (!*s) - return 0; - - s = strrchr(s, '/'); - if (!s) + int year = dmi_get_year(DMI_BIOS_DATE); + /* Doesn't exist? Likely an old system */ + if (year == -1) + return 1; + /* 0? Likely a buggy new BIOS */ + if (year == 0) return 0; - - s += 1; - - year = simple_strtoul(s, NULL, 0); - - if (year < 100) { /* 2-digit year */ - year += 1900; - if (year < 1996) /* no dates < spec 1.0 */ - year += 100; - } - if (year < CONFIG_ACPI_BLACKLIST_YEAR) { printk(KERN_ERR PREFIX "BIOS age (%d) fails cutoff (%d), " "acpi=force is required to enable ACPI\n", diff --git a/include/linux/dmi.h b/include/linux/dmi.h index 2e6bbe014157..64fd6c366604 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -68,6 +68,7 @@ extern char * dmi_get_system_info(int field); extern struct dmi_device * dmi_find_device(int type, const char *name, struct dmi_device *from); extern void dmi_scan_machine(void); +extern int dmi_get_year(int field); #else @@ -75,6 +76,7 @@ static inline int dmi_check_system(struct dmi_system_id *list) { return 0; } static inline char * dmi_get_system_info(int field) { return NULL; } static inline struct dmi_device * dmi_find_device(int type, const char *name, struct dmi_device *from) { return NULL; } +static inline int dmi_get_year(int year) { return 0; } #endif -- cgit v1.2.3 From f2d3efedbecc04dc348d723e4c90b46731b3bb48 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 25 Mar 2006 16:30:22 +0100 Subject: [PATCH] x86_64: Implement early DMI scanning There are more and more cases where we need to know DMI information early to work around bugs. i386 already had early DMI scanning, but x86-64 didn't. Implement this now. This required some cleanup in the i386 code. Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/i386/kernel/dmi_scan.c | 1 + arch/x86_64/kernel/setup.c | 16 +++++++++------- arch/x86_64/mm/init.c | 27 +++++++++++++++++++++++++++ include/asm-i386/dmi.h | 11 +++++++++++ include/asm-x86_64/dmi.h | 27 +++++++++++++++++++++++++++ include/asm-x86_64/io.h | 8 +++----- 6 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 include/asm-i386/dmi.h create mode 100644 include/asm-x86_64/dmi.h (limited to 'include') diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c index d2dfd9c8d691..ebc8dc116c43 100644 --- a/arch/i386/kernel/dmi_scan.c +++ b/arch/i386/kernel/dmi_scan.c @@ -5,6 +5,7 @@ #include #include #include +#include static char * __init dmi_string(struct dmi_header *dm, u8 s) { diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c index 49f285871df5..8acf5313e835 100644 --- a/arch/x86_64/kernel/setup.c +++ b/arch/x86_64/kernel/setup.c @@ -68,6 +68,7 @@ #include #include #include +#include /* * Machine setup.. @@ -92,6 +93,12 @@ int bootloader_type; unsigned long saved_video_mode; +/* + * Early DMI memory + */ +int dmi_alloc_index; +char dmi_alloc_data[DMI_MAX_DATA]; + /* * Setup options */ @@ -620,6 +627,8 @@ void __init setup_arch(char **cmdline_p) init_memory_mapping(0, (end_pfn_map << PAGE_SHIFT)); + dmi_scan_machine(); + zap_low_mappings(0); #ifdef CONFIG_ACPI @@ -1412,10 +1421,3 @@ struct seq_operations cpuinfo_op = { .show = show_cpuinfo, }; -static int __init run_dmi_scan(void) -{ - dmi_scan_machine(); - return 0; -} -core_initcall(run_dmi_scan); - diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c index 675a45691338..54c7f5975b44 100644 --- a/arch/x86_64/mm/init.c +++ b/arch/x86_64/mm/init.c @@ -225,6 +225,33 @@ static __meminit void unmap_low_page(int i) ti->allocated = 0; } +/* Must run before zap_low_mappings */ +__init void *early_ioremap(unsigned long addr, unsigned long size) +{ + unsigned long map = round_down(addr, LARGE_PAGE_SIZE); + + /* actually usually some more */ + if (size >= LARGE_PAGE_SIZE) { + printk("SMBIOS area too long %lu\n", size); + return NULL; + } + set_pmd(temp_mappings[0].pmd, __pmd(map | _KERNPG_TABLE | _PAGE_PSE)); + map += LARGE_PAGE_SIZE; + set_pmd(temp_mappings[1].pmd, __pmd(map | _KERNPG_TABLE | _PAGE_PSE)); + __flush_tlb(); + return temp_mappings[0].address + (addr & (LARGE_PAGE_SIZE-1)); +} + +/* To avoid virtual aliases later */ +__init void early_iounmap(void *addr, unsigned long size) +{ + if ((void *)round_down((unsigned long)addr, LARGE_PAGE_SIZE) != temp_mappings[0].address) + printk("early_iounmap: bad address %p\n", addr); + set_pmd(temp_mappings[0].pmd, __pmd(0)); + set_pmd(temp_mappings[1].pmd, __pmd(0)); + __flush_tlb(); +} + static void __meminit phys_pmd_init(pmd_t *pmd, unsigned long address, unsigned long end) { diff --git a/include/asm-i386/dmi.h b/include/asm-i386/dmi.h new file mode 100644 index 000000000000..38d4eeb7fc7e --- /dev/null +++ b/include/asm-i386/dmi.h @@ -0,0 +1,11 @@ +#ifndef _ASM_DMI_H +#define _ASM_DMI_H 1 + +#include + +/* Use early IO mappings for DMI because it's initialized early */ +#define dmi_ioremap bt_ioremap +#define dmi_iounmap bt_iounmap +#define dmi_alloc alloc_bootmem + +#endif diff --git a/include/asm-x86_64/dmi.h b/include/asm-x86_64/dmi.h new file mode 100644 index 000000000000..93b2b15d4325 --- /dev/null +++ b/include/asm-x86_64/dmi.h @@ -0,0 +1,27 @@ +#ifndef _ASM_DMI_H +#define _ASM_DMI_H 1 + +#include + +extern void *dmi_ioremap(unsigned long addr, unsigned long size); +extern void dmi_iounmap(void *addr, unsigned long size); + +#define DMI_MAX_DATA 2048 + +extern int dmi_alloc_index; +extern char dmi_alloc_data[DMI_MAX_DATA]; + +/* This is so early that there is no good way to allocate dynamic memory. + Allocate data in an BSS array. */ +static inline void *dmi_alloc(unsigned len) +{ + int idx = dmi_alloc_index; + if ((dmi_alloc_index += len) > DMI_MAX_DATA) + return NULL; + return dmi_alloc_data + idx; +} + +#define dmi_ioremap early_ioremap +#define dmi_iounmap early_iounmap + +#endif diff --git a/include/asm-x86_64/io.h b/include/asm-x86_64/io.h index a85fe8370820..ac12bda3bb1f 100644 --- a/include/asm-x86_64/io.h +++ b/include/asm-x86_64/io.h @@ -135,6 +135,9 @@ static inline void __iomem * ioremap (unsigned long offset, unsigned long size) return __ioremap(offset, size, 0); } +extern void *early_ioremap(unsigned long addr, unsigned long size); +extern void early_iounmap(void *addr, unsigned long size); + /* * This one maps high address device memory and turns off caching for that area. * it's useful if some control registers are in such an area and write combining @@ -143,11 +146,6 @@ static inline void __iomem * ioremap (unsigned long offset, unsigned long size) extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size); extern void iounmap(volatile void __iomem *addr); -/* Use normal IO mappings for DMI */ -#define dmi_ioremap ioremap -#define dmi_iounmap(x,l) iounmap(x) -#define dmi_alloc(l) kmalloc(l, GFP_ATOMIC) - /* * ISA I/O bus memory addresses are 1:1 with the physical address. */ -- cgit v1.2.3 From 554d284ba90bc2306c31e5363789f05c320969c3 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 25 Mar 2006 16:30:40 +0100 Subject: [PATCH] x86_64: Don't invoke OOM killer while allocating floppy DMA buffers Floppy can fall back to smaller buffers, so don't do OOM killing. Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- include/asm-x86_64/floppy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-x86_64/floppy.h b/include/asm-x86_64/floppy.h index af7ded63b517..52825ce689f2 100644 --- a/include/asm-x86_64/floppy.h +++ b/include/asm-x86_64/floppy.h @@ -155,7 +155,7 @@ static int fd_request_irq(void) static unsigned long dma_mem_alloc(unsigned long size) { - return __get_dma_pages(GFP_KERNEL,get_order(size)); + return __get_dma_pages(GFP_KERNEL|__GFP_NORETRY,get_order(size)); } -- cgit v1.2.3 From df92004ce60a0bb60c8315903c0765873c34a702 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Sat, 25 Mar 2006 16:31:01 +0100 Subject: [PATCH] x86_64: Reorder one field of the PDA to reduce padding This reorders the mmu_state int in the pda, such that there is no more padding (there currently is 4 bytes of padding). Boot tested. Signed-off-by: Arjan van de Ven Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- include/asm-x86_64/pda.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-x86_64/pda.h b/include/asm-x86_64/pda.h index c7ab38a601af..b47c3df9ed1d 100644 --- a/include/asm-x86_64/pda.h +++ b/include/asm-x86_64/pda.h @@ -22,8 +22,8 @@ struct x8664_pda { int nodenumber; /* number of current node */ unsigned int __softirq_pending; unsigned int __nmi_count; /* number of NMI on this CPUs */ - struct mm_struct *active_mm; int mmu_state; + struct mm_struct *active_mm; unsigned apic_timer_irqs; } ____cacheline_aligned_in_smp; -- cgit v1.2.3 From 267b48014a5c0c2ae90b04dad5d95ceb903365a6 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 25 Mar 2006 16:31:10 +0100 Subject: [PATCH] x86_64: Try to allocate node memmap near the end of node This fixes problems with very large nodes (over 128GB) filling up all of the first 4GB with their mem_map and not leaving enough space for the swiotlb. Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/x86_64/mm/numa.c | 12 +++++++++++- include/linux/bootmem.h | 3 +++ mm/bootmem.c | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86_64/mm/numa.c b/arch/x86_64/mm/numa.c index e4b62753a19a..07471a3eb190 100644 --- a/arch/x86_64/mm/numa.c +++ b/arch/x86_64/mm/numa.c @@ -149,7 +149,7 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long en /* Initialize final allocator for a zone */ void __init setup_node_zones(int nodeid) { - unsigned long start_pfn, end_pfn; + unsigned long start_pfn, end_pfn, memmapsize, limit; unsigned long zones[MAX_NR_ZONES]; unsigned long holes[MAX_NR_ZONES]; @@ -159,6 +159,16 @@ void __init setup_node_zones(int nodeid) Dprintk(KERN_INFO "Setting up node %d %lx-%lx\n", nodeid, start_pfn, end_pfn); + /* Try to allocate mem_map at end to not fill up precious <4GB + memory. */ + memmapsize = sizeof(struct page) * (end_pfn-start_pfn); + limit = end_pfn << PAGE_SHIFT; + NODE_DATA(nodeid)->node_mem_map = + __alloc_bootmem_core(NODE_DATA(nodeid)->bdata, + memmapsize, SMP_CACHE_BYTES, + round_down(limit - memmapsize, PAGE_SIZE), + limit); + size_zones(zones, holes, start_pfn, end_pfn); free_area_init_node(nodeid, NODE_DATA(nodeid), zones, start_pfn, holes); diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 993da8cc9706..7155452fb4a8 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -51,6 +51,9 @@ extern void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal); +extern void * __init __alloc_bootmem_core(struct bootmem_data *bdata, + unsigned long size, unsigned long align, unsigned long goal, + unsigned long limit); #ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE extern void __init reserve_bootmem (unsigned long addr, unsigned long size); #define alloc_bootmem(x) \ diff --git a/mm/bootmem.c b/mm/bootmem.c index 35c32290f717..b55bd39fc5dd 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -152,7 +152,7 @@ static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, * * NOTE: This function is _not_ reentrant. */ -static void * __init +void * __init __alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, unsigned long align, unsigned long goal, unsigned long limit) { -- cgit v1.2.3 From da7ed9f98f6f3f18664f8ab24303f9428b9d78f8 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Sat, 25 Mar 2006 16:31:16 +0100 Subject: [PATCH] x86_64: timer interrupt lockup due to pending interrupt o check_timer() routine fails while second kernel is booting after a crash on an opetron box. Problem happens because timer vector (0x31) seems to be locked. o After a system crash, it is not safe to service interrupts any more, hence interrupts are disabled. This leads to pending interrupts at LAPIC. LAPIC sends these interrupts to the CPU during early boot of second kernel. Other pending interrupts are discarded saying unexpected trap but timer interrupt is serviced and CPU does not issue an LAPIC EOI because it think this interrupt came from i8259 and sends ack to 8259. This leads to vector 0x31 locking as LAPIC does not clear respective ISR and keeps on waiting for EOI. o This patch issues extra EOI for the pending interrupts who have ISR set. o Though today only timer seems to be the special case because in early boot it thinks interrupts are coming from i8259 and uses mask_and_ack_8259A() as ack handler and does not issue LAPIC EOI. But probably doing it in generic manner for all vectors makes sense. Signed-off-by: Vivek Goyal Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/x86_64/kernel/apic.c | 20 ++++++++++++++++++++ include/asm-x86_64/apicdef.h | 1 + 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c index e5b14c57eaa0..d54620147e8e 100644 --- a/arch/x86_64/kernel/apic.c +++ b/arch/x86_64/kernel/apic.c @@ -342,6 +342,7 @@ void __init init_bsp_APIC(void) void __cpuinit setup_local_APIC (void) { unsigned int value, maxlvt; + int i, j; value = apic_read(APIC_LVR); @@ -370,6 +371,25 @@ void __cpuinit setup_local_APIC (void) value &= ~APIC_TPRI_MASK; apic_write(APIC_TASKPRI, value); + /* + * After a crash, we no longer service the interrupts and a pending + * interrupt from previous kernel might still have ISR bit set. + * + * Most probably by now CPU has serviced that pending interrupt and + * it might not have done the ack_APIC_irq() because it thought, + * interrupt came from i8259 as ExtInt. LAPIC did not get EOI so it + * does not clear the ISR bit and cpu thinks it has already serivced + * the interrupt. Hence a vector might get locked. It was noticed + * for timer irq (vector 0x31). Issue an extra EOI to clear ISR. + */ + for (i = APIC_ISR_NR - 1; i >= 0; i--) { + value = apic_read(APIC_ISR + i*0x10); + for (j = 31; j >= 0; j--) { + if (value & (1< Date: Sat, 25 Mar 2006 16:31:31 +0100 Subject: [PATCH] x86_64: Remove CONFIG_UNORDERED_IO It was a failed experiment - all benchmarks done with it on both AMD and Intel showed it was a loss. That was probably because the store buffers of the CPUs for write combining traffic weren't large enough. Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/x86_64/Kconfig | 10 ---------- include/asm-x86_64/io.h | 18 ------------------ 2 files changed, 28 deletions(-) (limited to 'include') diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 6fc631457196..31bab721cb71 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -520,16 +520,6 @@ config PCI_MMCONFIG bool "Support mmconfig PCI config space access" depends on PCI && ACPI -config UNORDERED_IO - bool "Unordered IO mapping access" - depends on EXPERIMENTAL - help - Use unordered stores to access IO memory mappings in device drivers. - Still very experimental. When a driver works on IA64/ppc64/pa-risc it should - work with this option, but it makes the drivers behave differently - from i386. Requires that the driver writer used memory barriers - properly. - source "drivers/pci/pcie/Kconfig" source "drivers/pci/Kconfig" diff --git a/include/asm-x86_64/io.h b/include/asm-x86_64/io.h index ac12bda3bb1f..cafdfb37f0d8 100644 --- a/include/asm-x86_64/io.h +++ b/include/asm-x86_64/io.h @@ -200,23 +200,6 @@ static inline __u64 __readq(const volatile void __iomem *addr) #define mmiowb() -#ifdef CONFIG_UNORDERED_IO -static inline void __writel(__u32 val, volatile void __iomem *addr) -{ - volatile __u32 __iomem *target = addr; - asm volatile("movnti %1,%0" - : "=m" (*target) - : "r" (val) : "memory"); -} - -static inline void __writeq(__u64 val, volatile void __iomem *addr) -{ - volatile __u64 __iomem *target = addr; - asm volatile("movnti %1,%0" - : "=m" (*target) - : "r" (val) : "memory"); -} -#else static inline void __writel(__u32 b, volatile void __iomem *addr) { *(__force volatile __u32 *)addr = b; @@ -225,7 +208,6 @@ static inline void __writeq(__u64 b, volatile void __iomem *addr) { *(__force volatile __u64 *)addr = b; } -#endif static inline void __writeb(__u8 b, volatile void __iomem *addr) { *(__force volatile __u8 *)addr = b; -- cgit v1.2.3 From 94949436191f69dac735919a9698612ef6b3dbba Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 25 Mar 2006 16:31:37 +0100 Subject: [PATCH] x86_64: Make local_t 64bit instead of 32bit For consistency with other architectures Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- include/asm-x86_64/local.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/asm-x86_64/local.h b/include/asm-x86_64/local.h index 3e72c41727c5..bf148037d4e5 100644 --- a/include/asm-x86_64/local.h +++ b/include/asm-x86_64/local.h @@ -5,7 +5,7 @@ typedef struct { - volatile unsigned int counter; + volatile unsigned long counter; } local_t; #define LOCAL_INIT(i) { (i) } @@ -16,7 +16,7 @@ typedef struct static __inline__ void local_inc(local_t *v) { __asm__ __volatile__( - "incl %0" + "incq %0" :"=m" (v->counter) :"m" (v->counter)); } @@ -24,7 +24,7 @@ static __inline__ void local_inc(local_t *v) static __inline__ void local_dec(local_t *v) { __asm__ __volatile__( - "decl %0" + "decq %0" :"=m" (v->counter) :"m" (v->counter)); } @@ -32,7 +32,7 @@ static __inline__ void local_dec(local_t *v) static __inline__ void local_add(unsigned int i, local_t *v) { __asm__ __volatile__( - "addl %1,%0" + "addq %1,%0" :"=m" (v->counter) :"ir" (i), "m" (v->counter)); } @@ -40,7 +40,7 @@ static __inline__ void local_add(unsigned int i, local_t *v) static __inline__ void local_sub(unsigned int i, local_t *v) { __asm__ __volatile__( - "subl %1,%0" + "subq %1,%0" :"=m" (v->counter) :"ir" (i), "m" (v->counter)); } -- cgit v1.2.3 From dcf36bfa5de6d4e37878d4c98b6986fee4eb8b4c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 25 Mar 2006 16:31:46 +0100 Subject: [PATCH] x86_64: group memnodemap and memnodeshift in a memnode structure pfn_to_page() and others need to access both memnode_shift and the very first bytes of memnodemap[]. If we force memnode_shift to be just before the memnodemap array, we can reduce the memory footprint to one cache line instead of two for most setups. This patch introduce a 'memnode' structure where shift and map[] are carefully placed. Signed-off-by: Eric Dumazet Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/x86_64/mm/numa.c | 6 ++---- include/asm-x86_64/mmzone.h | 9 +++++++-- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/x86_64/mm/numa.c b/arch/x86_64/mm/numa.c index 07471a3eb190..63c72641b737 100644 --- a/arch/x86_64/mm/numa.c +++ b/arch/x86_64/mm/numa.c @@ -25,8 +25,7 @@ struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; bootmem_data_t plat_node_bdata[MAX_NUMNODES]; -int memnode_shift; -u8 memnodemap[NODEMAPSIZE]; +struct memnode memnode; unsigned char cpu_to_node[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = NUMA_NO_NODE @@ -367,8 +366,7 @@ void __init init_cpu_to_node(void) EXPORT_SYMBOL(cpu_to_node); EXPORT_SYMBOL(node_to_cpumask); -EXPORT_SYMBOL(memnode_shift); -EXPORT_SYMBOL(memnodemap); +EXPORT_SYMBOL(memnode); EXPORT_SYMBOL(node_data); #ifdef CONFIG_DISCONTIGMEM diff --git a/include/asm-x86_64/mmzone.h b/include/asm-x86_64/mmzone.h index 972c9359f7d7..937f99b26883 100644 --- a/include/asm-x86_64/mmzone.h +++ b/include/asm-x86_64/mmzone.h @@ -15,8 +15,13 @@ #define NODEMAPSIZE 0xfff /* Simple perfect hash to map physical addresses to node numbers */ -extern int memnode_shift; -extern u8 memnodemap[NODEMAPSIZE]; +struct memnode { + int shift; + u8 map[NODEMAPSIZE]; +} ____cacheline_aligned; +extern struct memnode memnode; +#define memnode_shift memnode.shift +#define memnodemap memnode.map extern struct pglist_data *node_data[]; -- cgit v1.2.3 From f271a6f557497830f3995138b0c78d8634b33863 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Sat, 25 Mar 2006 16:31:58 +0100 Subject: [PATCH] x86_64: Removed duplicated declaration of force_iommu Noticed by Andrew Morton. Signed-off-by: Andi Kleen Signed-off-by: Linus Torvalds --- include/asm-x86_64/proto.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/asm-x86_64/proto.h b/include/asm-x86_64/proto.h index 977fe58e971c..8abf2a43c944 100644 --- a/include/asm-x86_64/proto.h +++ b/include/asm-x86_64/proto.h @@ -127,7 +127,6 @@ extern int fix_aperture; #define iommu_aperture 0 #define iommu_aperture_allowed 0 #endif -extern int force_iommu; extern int reboot_force; extern int notsc_setup(char *); -- cgit v1.2.3 From 3cbb90a9cb7854b1110663919d5bc3da3f46d5e3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 25 Mar 2006 09:41:40 -0800 Subject: powerpc: fix strncasecmp prototype It takes a size_t, not an int, as its third argument. Signed-off-by: Linus Torvalds --- arch/powerpc/lib/strcase.c | 4 +++- include/asm-powerpc/string.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/powerpc/lib/strcase.c b/arch/powerpc/lib/strcase.c index 36b521091bbc..f8ec1eba3fdd 100644 --- a/arch/powerpc/lib/strcase.c +++ b/arch/powerpc/lib/strcase.c @@ -1,4 +1,6 @@ +#include #include +#include int strcasecmp(const char *s1, const char *s2) { @@ -11,7 +13,7 @@ int strcasecmp(const char *s1, const char *s2) return c1 - c2; } -int strncasecmp(const char *s1, const char *s2, int n) +int strncasecmp(const char *s1, const char *s2, size_t n) { int c1, c2; diff --git a/include/asm-powerpc/string.h b/include/asm-powerpc/string.h index 8606a696c088..faa407f33c6b 100644 --- a/include/asm-powerpc/string.h +++ b/include/asm-powerpc/string.h @@ -15,7 +15,7 @@ #define __HAVE_ARCH_MEMCHR extern int strcasecmp(const char *, const char *); -extern int strncasecmp(const char *, const char *, int); +extern int strncasecmp(const char *, const char *, __kernel_size_t); extern char * strcpy(char *,const char *); extern char * strncpy(char *,const char *, __kernel_size_t); extern __kernel_size_t strlen(const char *); -- cgit v1.2.3 From 686f8c5d77149f78ff6090dde774b2e43a7319b2 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Sat, 25 Mar 2006 18:15:24 +0000 Subject: include/linux/clk.h is betraying its ARM origins include/linux/clk.h is betraying its ARM origins. Signed-off-by: Todd Poynor Signed-off-by: Russell King --- include/linux/clk.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/clk.h b/include/linux/clk.h index 12848f81bb37..5ca8c6fddb56 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -8,8 +8,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#ifndef ASMARM_CLOCK_H -#define ASMARM_CLOCK_H +#ifndef __LINUX_CLK_H +#define __LINUX_CLK_H struct device; -- cgit v1.2.3 From 1310eda4bec331fd951a8cbe80619f050f9036fc Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Sat, 25 Mar 2006 21:57:59 +0000 Subject: [ARM] 3397/1: AT91RM9200 Header update Patch from Andrew Victor This patch updates the hardware header to include definitions for the Memory Controller registers. Signed-off-by: Andrew Victor Signed-off-by: Russell King --- include/asm-arm/arch-at91rm9200/at91rm9200_sys.h | 100 ++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-arm/arch-at91rm9200/at91rm9200_sys.h b/include/asm-arm/arch-at91rm9200/at91rm9200_sys.h index 9bfffdbf1e0b..2910d359f919 100644 --- a/include/asm-arm/arch-at91rm9200/at91rm9200_sys.h +++ b/include/asm-arm/arch-at91rm9200/at91rm9200_sys.h @@ -172,6 +172,7 @@ #define AT91_PMC_MDIV_4 (3 << 8) #define AT91_PMC_PCKR(n) (AT91_PMC + 0x40 + ((n) * 4)) /* Programmable Clock 0-3 Registers */ + #define AT91_PMC_IER (AT91_PMC + 0x60) /* Interrupt Enable Register */ #define AT91_PMC_IDR (AT91_PMC + 0x64) /* Interrupt Disable Register */ #define AT91_PMC_SR (AT91_PMC + 0x68) /* Status Register */ @@ -286,8 +287,32 @@ #define AT91_MC_RCB (1 << 0) /* Remap Command Bit */ #define AT91_MC_ASR (AT91_MC + 0x04) /* MC Abort Status Register */ +#define AT91_MC_UNADD (1 << 0) /* Undefined Address Abort Status */ +#define AT91_MC_MISADD (1 << 1) /* Misaligned Address Abort Status */ +#define AT91_MC_ABTSZ (3 << 8) /* Abort Size Status */ +#define AT91_MC_ABTSZ_BYTE (0 << 8) +#define AT91_MC_ABTSZ_HALFWORD (1 << 8) +#define AT91_MC_ABTSZ_WORD (2 << 8) +#define AT91_MC_ABTTYP (3 << 10) /* Abort Type Status */ +#define AT91_MC_ABTTYP_DATAREAD (0 << 10) +#define AT91_MC_ABTTYP_DATAWRITE (1 << 10) +#define AT91_MC_ABTTYP_FETCH (2 << 10) +#define AT91_MC_MST0 (1 << 16) /* ARM920T Abort Source */ +#define AT91_MC_MST1 (1 << 17) /* PDC Abort Source */ +#define AT91_MC_MST2 (1 << 18) /* UHP Abort Source */ +#define AT91_MC_MST3 (1 << 19) /* EMAC Abort Source */ +#define AT91_MC_SVMST0 (1 << 24) /* Saved ARM920T Abort Source */ +#define AT91_MC_SVMST1 (1 << 25) /* Saved PDC Abort Source */ +#define AT91_MC_SVMST2 (1 << 26) /* Saved UHP Abort Source */ +#define AT91_MC_SVMST3 (1 << 27) /* Saved EMAC Abort Source */ + #define AT91_MC_AASR (AT91_MC + 0x08) /* MC Abort Address Status Register */ + #define AT91_MC_MPR (AT91_MC + 0x0c) /* MC Master Priority Register */ +#define AT91_MPR_MSTP0 (7 << 0) /* ARM920T Priority */ +#define AT91_MPR_MSTP1 (7 << 4) /* PDC Priority */ +#define AT91_MPR_MSTP2 (7 << 8) /* UHP Priority */ +#define AT91_MPR_MSTP3 (7 << 12) /* EMAC Priority */ /* External Bus Interface (EBI) registers */ #define AT91_EBI_CSA (AT91_MC + 0x60) /* Chip Select Assignment Register */ @@ -309,8 +334,10 @@ /* Static Memory Controller (SMC) registers */ #define AT91_SMC_CSR(n) (AT91_MC + 0x70 + ((n) * 4))/* SMC Chip Select Register */ #define AT91_SMC_NWS (0x7f << 0) /* Number of Wait States */ +#define AT91_SMC_NWS_(x) ((x) << 0) #define AT91_SMC_WSEN (1 << 7) /* Wait State Enable */ #define AT91_SMC_TDF (0xf << 8) /* Data Float Time */ +#define AT91_SMC_TDF_(x) ((x) << 8) #define AT91_SMC_BAT (1 << 12) /* Byte Access Type */ #define AT91_SMC_DBW (3 << 13) /* Data Bus Width */ #define AT91_SMC_DBW_16 (1 << 13) @@ -322,7 +349,78 @@ #define AT91_SMC_ACSS_2 (2 << 16) #define AT91_SMC_ACSS_3 (3 << 16) #define AT91_SMC_RWSETUP (7 << 24) /* Read & Write Signal Time Setup */ +#define AT91_SMC_RWSETUP_(x) ((x) << 24) #define AT91_SMC_RWHOLD (7 << 28) /* Read & Write Signal Hold Time */ - +#define AT91_SMC_RWHOLD_(x) ((x) << 28) + +/* SDRAM Controller registers */ +#define AT91_SDRAMC_MR (AT91_MC + 0x90) /* Mode Register */ +#define AT91_SDRAMC_MODE (0xf << 0) /* Command Mode */ +#define AT91_SDRAMC_MODE_NORMAL (0 << 0) +#define AT91_SDRAMC_MODE_NOP (1 << 0) +#define AT91_SDRAMC_MODE_PRECHARGE (2 << 0) +#define AT91_SDRAMC_MODE_LMR (3 << 0) +#define AT91_SDRAMC_MODE_REFRESH (4 << 0) +#define AT91_SDRAMC_DBW (1 << 4) /* Data Bus Width */ +#define AT91_SDRAMC_DBW_32 (0 << 4) +#define AT91_SDRAMC_DBW_16 (1 << 4) + +#define AT91_SDRAMC_TR (AT91_MC + 0x94) /* Refresh Timer Register */ +#define AT91_SDRAMC_COUNT (0xfff << 0) /* Refresh Timer Count */ + +#define AT91_SDRAMC_CR (AT91_MC + 0x98) /* Configuration Register */ +#define AT91_SDRAMC_NC (3 << 0) /* Number of Column Bits */ +#define AT91_SDRAMC_NC_8 (0 << 0) +#define AT91_SDRAMC_NC_9 (1 << 0) +#define AT91_SDRAMC_NC_10 (2 << 0) +#define AT91_SDRAMC_NC_11 (3 << 0) +#define AT91_SDRAMC_NR (3 << 2) /* Number of Row Bits */ +#define AT91_SDRAMC_NR_11 (0 << 2) +#define AT91_SDRAMC_NR_12 (1 << 2) +#define AT91_SDRAMC_NR_13 (2 << 2) +#define AT91_SDRAMC_NB (1 << 4) /* Number of Banks */ +#define AT91_SDRAMC_NB_2 (0 << 4) +#define AT91_SDRAMC_NB_4 (1 << 4) +#define AT91_SDRAMC_CAS (3 << 5) /* CAS Latency */ +#define AT91_SDRAMC_CAS_2 (2 << 5) +#define AT91_SDRAMC_TWR (0xf << 7) /* Write Recovery Delay */ +#define AT91_SDRAMC_TRC (0xf << 11) /* Row Cycle Delay */ +#define AT91_SDRAMC_TRP (0xf << 15) /* Row Precharge Delay */ +#define AT91_SDRAMC_TRCD (0xf << 19) /* Row to Column Delay */ +#define AT91_SDRAMC_TRAS (0xf << 23) /* Active to Precharge Delay */ +#define AT91_SDRAMC_TXSR (0xf << 27) /* Exit Self Refresh to Active Delay */ + +#define AT91_SDRAMC_SRR (AT91_MC + 0x9c) /* Self Refresh Register */ +#define AT91_SDRAMC_LPR (AT91_MC + 0xa0) /* Low Power Register */ +#define AT91_SDRAMC_IER (AT91_MC + 0xa4) /* Interrupt Enable Register */ +#define AT91_SDRAMC_IDR (AT91_MC + 0xa8) /* Interrupt Disable Register */ +#define AT91_SDRAMC_IMR (AT91_MC + 0xac) /* Interrupt Mask Register */ +#define AT91_SDRAMC_ISR (AT91_MC + 0xb0) /* Interrupt Status Register */ + +/* Burst Flash Controller register */ +#define AT91_BFC_MR (AT91_MC + 0xc0) /* Mode Register */ +#define AT91_BFC_BFCOM (3 << 0) /* Burst Flash Controller Operating Mode */ +#define AT91_BFC_BFCOM_DISABLED (0 << 0) +#define AT91_BFC_BFCOM_ASYNC (1 << 0) +#define AT91_BFC_BFCOM_BURST (2 << 0) +#define AT91_BFC_BFCC (3 << 2) /* Burst Flash Controller Clock */ +#define AT91_BFC_BFCC_MCK (1 << 2) +#define AT91_BFC_BFCC_DIV2 (2 << 2) +#define AT91_BFC_BFCC_DIV4 (3 << 2) +#define AT91_BFC_AVL (0xf << 4) /* Address Valid Latency */ +#define AT91_BFC_PAGES (7 << 8) /* Page Size */ +#define AT91_BFC_PAGES_NO_PAGE (0 << 8) +#define AT91_BFC_PAGES_16 (1 << 8) +#define AT91_BFC_PAGES_32 (2 << 8) +#define AT91_BFC_PAGES_64 (3 << 8) +#define AT91_BFC_PAGES_128 (4 << 8) +#define AT91_BFC_PAGES_256 (5 << 8) +#define AT91_BFC_PAGES_512 (6 << 8) +#define AT91_BFC_PAGES_1024 (7 << 8) +#define AT91_BFC_OEL (3 << 12) /* Output Enable Latency */ +#define AT91_BFC_BAAEN (1 << 16) /* Burst Address Advance Enable */ +#define AT91_BFC_BFOEH (1 << 17) /* Burst Flash Output Enable Handling */ +#define AT91_BFC_MUXEN (1 << 18) /* Multiplexed Bus Enable */ +#define AT91_BFC_RDYEN (1 << 19) /* Ready Enable Mode */ #endif -- cgit v1.2.3 From 104c7b03ea0913a24be103db66d8cf1f1f99a49a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Sat, 25 Mar 2006 23:03:13 +0000 Subject: [ARM] 3383/3: ixp2000: ixdp2x01 platform serial conversion Patch from Lennert Buytenhek Add a PLAT8250_DEV_PLATFORM2, and convert the two ixdp2x01 CPLD serial ports to use platform serial devices with ids PLAT8250_DEV_PLATFORM[12]. (The on-chip xscale UART is PLAT8250_DEV_PLATFORM, id #0.) Signed-off-by: Lennert Buytenhek Signed-off-by: Russell King --- arch/arm/mach-ixp2000/ixdp2x01.c | 78 ++++++++++++++++++++++++++++++---------- include/linux/serial_8250.h | 1 + 2 files changed, 60 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-ixp2000/ixdp2x01.c b/arch/arm/mach-ixp2000/ixdp2x01.c index f9d4968c1d66..66915282a463 100644 --- a/arch/arm/mach-ixp2000/ixdp2x01.c +++ b/arch/arm/mach-ixp2000/ixdp2x01.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -132,7 +133,7 @@ void __init ixdp2x01_init_irq(void) /************************************************************************* - * IXDP2x01 memory map and serial ports + * IXDP2x01 memory map *************************************************************************/ static struct map_desc ixdp2x01_io_desc __initdata = { .virtual = IXDP2X01_VIRT_CPLD_BASE, @@ -141,40 +142,78 @@ static struct map_desc ixdp2x01_io_desc __initdata = { .type = MT_DEVICE }; -static struct uart_port ixdp2x01_serial_ports[2] = { +static void __init ixdp2x01_map_io(void) +{ + ixp2000_map_io(); + iotable_init(&ixdp2x01_io_desc, 1); +} + + +/************************************************************************* + * IXDP2x01 serial ports + *************************************************************************/ +static struct plat_serial8250_port ixdp2x01_serial_port1[] = { { - .membase = (char *)(IXDP2X01_UART1_VIRT_BASE), .mapbase = (unsigned long)IXDP2X01_UART1_PHYS_BASE, + .membase = (char *)IXDP2X01_UART1_VIRT_BASE, .irq = IRQ_IXDP2X01_UART1, - .flags = UPF_SKIP_TEST, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, .iotype = UPIO_MEM32, .regshift = 2, .uartclk = IXDP2X01_UART_CLK, - .line = 1, - .type = PORT_16550A, - .fifosize = 16 - }, { - .membase = (char *)(IXDP2X01_UART2_VIRT_BASE), + }, + { } +}; + +static struct resource ixdp2x01_uart_resource1 = { + .start = IXDP2X01_UART1_PHYS_BASE, + .end = IXDP2X01_UART1_PHYS_BASE + 0xffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device ixdp2x01_serial_device1 = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM1, + .dev = { + .platform_data = ixdp2x01_serial_port1, + }, + .num_resources = 1, + .resource = &ixdp2x01_uart_resource1, +}; + +static struct plat_serial8250_port ixdp2x01_serial_port2[] = { + { .mapbase = (unsigned long)IXDP2X01_UART2_PHYS_BASE, + .membase = (char *)IXDP2X01_UART2_VIRT_BASE, .irq = IRQ_IXDP2X01_UART2, - .flags = UPF_SKIP_TEST, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, .iotype = UPIO_MEM32, .regshift = 2, .uartclk = IXDP2X01_UART_CLK, - .line = 2, - .type = PORT_16550A, - .fifosize = 16 }, + { } }; -static void __init ixdp2x01_map_io(void) -{ - ixp2000_map_io(); +static struct resource ixdp2x01_uart_resource2 = { + .start = IXDP2X01_UART2_PHYS_BASE, + .end = IXDP2X01_UART2_PHYS_BASE + 0xffff, + .flags = IORESOURCE_MEM, +}; - iotable_init(&ixdp2x01_io_desc, 1); +static struct platform_device ixdp2x01_serial_device2 = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM2, + .dev = { + .platform_data = ixdp2x01_serial_port2, + }, + .num_resources = 1, + .resource = &ixdp2x01_uart_resource2, +}; - early_serial_setup(&ixdp2x01_serial_ports[0]); - early_serial_setup(&ixdp2x01_serial_ports[1]); +static void ixdp2x01_uart_init(void) +{ + platform_device_register(&ixdp2x01_serial_device1); + platform_device_register(&ixdp2x01_serial_device2); } @@ -374,6 +413,7 @@ static void __init ixdp2x01_init_machine(void) platform_add_devices(ixdp2x01_devices, ARRAY_SIZE(ixdp2x01_devices)); ixp2000_uart_init(); + ixdp2x01_uart_init(); } diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 73b464f0926a..8e9681413726 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -37,6 +37,7 @@ enum { PLAT8250_DEV_LEGACY = -1, PLAT8250_DEV_PLATFORM, PLAT8250_DEV_PLATFORM1, + PLAT8250_DEV_PLATFORM2, PLAT8250_DEV_FOURPORT, PLAT8250_DEV_ACCENT, PLAT8250_DEV_BOCA, -- cgit v1.2.3 From 64a07bd82ed526d813b64b0957543eef55bdf9c0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Sun, 26 Mar 2006 01:36:55 -0800 Subject: [PATCH] protect remove_proc_entry It has been discovered that the remove_proc_entry has a race in the removing of entries in the proc file system that are siblings. There's no protection around the traversing and removing of elements that belong in the same subdirectory. This subdirectory list is protected in other areas by the BKL. So the BKL was at first used to protect this area too, but unfortunately, remove_proc_entry may be called with spinlocks held. The BKL may schedule, so this was not a solution. The final solution was to add a new global spin lock to protect this list, called proc_subdir_lock. This lock now protects the list in remove_proc_entry, and I also went around looking for other areas that this list is modified and added this protection there too. Care must be taken since these locations call several functions that may also schedule. Since I don't see any location that these functions that modify the subdirectory list are called by interrupts, the irqsave/restore versions of the spin lock was _not_ used. Signed-off-by: Steven Rostedt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/generic.c | 32 +++++++++++++++++++++++++++++--- fs/proc/proc_devtree.c | 2 ++ include/linux/proc_fs.h | 3 +++ 3 files changed, 34 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 20e5c4509a43..47b7a20d45eb 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "internal.h" @@ -29,6 +30,8 @@ static ssize_t proc_file_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); static loff_t proc_file_lseek(struct file *, loff_t, int); +DEFINE_SPINLOCK(proc_subdir_lock); + int proc_match(int len, const char *name, struct proc_dir_entry *de) { if (de->namelen != len) @@ -277,7 +280,9 @@ static int xlate_proc_name(const char *name, const char *cp = name, *next; struct proc_dir_entry *de; int len; + int rtn = 0; + spin_lock(&proc_subdir_lock); de = &proc_root; while (1) { next = strchr(cp, '/'); @@ -289,13 +294,17 @@ static int xlate_proc_name(const char *name, if (proc_match(len, cp, de)) break; } - if (!de) - return -ENOENT; + if (!de) { + rtn = -ENOENT; + goto out; + } cp += len + 1; } *residual = cp; *ret = de; - return 0; +out: + spin_unlock(&proc_subdir_lock); + return rtn; } static DEFINE_IDR(proc_inum_idr); @@ -380,6 +389,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam int error = -ENOENT; lock_kernel(); + spin_lock(&proc_subdir_lock); de = PDE(dir); if (de) { for (de = de->subdir; de ; de = de->next) { @@ -388,12 +398,15 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { unsigned int ino = de->low_ino; + spin_unlock(&proc_subdir_lock); error = -EINVAL; inode = proc_get_inode(dir->i_sb, ino, de); + spin_lock(&proc_subdir_lock); break; } } } + spin_unlock(&proc_subdir_lock); unlock_kernel(); if (inode) { @@ -447,11 +460,13 @@ int proc_readdir(struct file * filp, filp->f_pos++; /* fall through */ default: + spin_lock(&proc_subdir_lock); de = de->subdir; i -= 2; for (;;) { if (!de) { ret = 1; + spin_unlock(&proc_subdir_lock); goto out; } if (!i) @@ -461,12 +476,16 @@ int proc_readdir(struct file * filp, } do { + /* filldir passes info to user space */ + spin_unlock(&proc_subdir_lock); if (filldir(dirent, de->name, de->namelen, filp->f_pos, de->low_ino, de->mode >> 12) < 0) goto out; + spin_lock(&proc_subdir_lock); filp->f_pos++; de = de->next; } while (de); + spin_unlock(&proc_subdir_lock); } ret = 1; out: unlock_kernel(); @@ -500,9 +519,13 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp if (i == 0) return -EAGAIN; dp->low_ino = i; + + spin_lock(&proc_subdir_lock); dp->next = dir->subdir; dp->parent = dir; dir->subdir = dp; + spin_unlock(&proc_subdir_lock); + if (S_ISDIR(dp->mode)) { if (dp->proc_iops == NULL) { dp->proc_fops = &proc_dir_operations; @@ -694,6 +717,8 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) if (!parent && xlate_proc_name(name, &parent, &fn) != 0) goto out; len = strlen(fn); + + spin_lock(&proc_subdir_lock); for (p = &parent->subdir; *p; p=&(*p)->next ) { if (!proc_match(len, fn, *p)) continue; @@ -714,6 +739,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) } break; } + spin_unlock(&proc_subdir_lock); out: return; } diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 9bdd077d6f55..596b4b4f1cc8 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -136,9 +136,11 @@ void proc_device_tree_add_node(struct device_node *np, * properties are quite unimportant for us though, thus we * simply "skip" them here, but we do have to check. */ + spin_lock(&proc_subdir_lock); for (ent = de->subdir; ent != NULL; ent = ent->next) if (!strcmp(ent->name, pp->name)) break; + spin_unlock(&proc_subdir_lock); if (ent != NULL) { printk(KERN_WARNING "device-tree: property \"%s\" name" " conflicts with node in %s\n", pp->name, diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index aa6322d45198..6b12b0f661b4 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -4,6 +4,7 @@ #include #include #include +#include #include /* @@ -92,6 +93,8 @@ extern struct proc_dir_entry *proc_bus; extern struct proc_dir_entry *proc_root_driver; extern struct proc_dir_entry *proc_root_kcore; +extern spinlock_t proc_subdir_lock; + extern void proc_root_init(void); extern void proc_misc_init(void); -- cgit v1.2.3 From 03beb07664d768db97bf454ae5c9581cd4737bb4 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 26 Mar 2006 01:36:57 -0800 Subject: [PATCH] Add API for flushing Anon pages Currently, get_user_pages() returns fully coherent pages to the kernel for anything other than anonymous pages. This is a problem for things like fuse and the SCSI generic ioctl SG_IO which can potentially wish to do DMA to anonymous pages passed in by users. The fix is to add a new memory management API: flush_anon_page() which is used in get_user_pages() to make anonymous pages coherent. Signed-off-by: James Bottomley Cc: Russell King Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cachetlb.txt | 9 +++++++++ include/linux/highmem.h | 6 ++++++ mm/memory.c | 2 ++ 3 files changed, 17 insertions(+) (limited to 'include') diff --git a/Documentation/cachetlb.txt b/Documentation/cachetlb.txt index 4ae418889b88..1f312a9893d9 100644 --- a/Documentation/cachetlb.txt +++ b/Documentation/cachetlb.txt @@ -362,6 +362,15 @@ maps this page at its virtual address. likely that you will need to flush the instruction cache for copy_to_user_page(). + void flush_anon_page(struct page *page, unsigned long vmaddr) + When the kernel needs to access the contents of an anonymous + page, it calls this function (currently only + get_user_pages()). Note: flush_dcache_page() deliberately + doesn't work for an anonymous page. The default + implementation is a nop (and should remain so for all coherent + architectures). For incoherent architectures, it should flush + the cache of the page at vmaddr in the current user process. + void flush_icache_range(unsigned long start, unsigned long end) When the kernel stores into addresses that it will execute out of (eg when loading modules), this function is called. diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 6bece9280eb7..7bd2593dbef9 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -7,6 +7,12 @@ #include +#ifndef ARCH_HAS_FLUSH_ANON_PAGE +static inline void flush_anon_page(struct page *page, unsigned long vmaddr) +{ +} +#endif + #ifdef CONFIG_HIGHMEM #include diff --git a/mm/memory.c b/mm/memory.c index e347e106ca3a..67686048f094 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1071,6 +1071,8 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, } if (pages) { pages[i] = page; + + flush_anon_page(page, start); flush_dcache_page(page); } if (vmas) -- cgit v1.2.3 From 5a3a5a98b6422d05c39eaa32c8b3f83840c7b768 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 26 Mar 2006 01:36:59 -0800 Subject: [PATCH] Add flush_kernel_dcache_page() API We have a problem in a lot of emulated storage in that it takes a page from get_user_pages() and does something like kmap_atomic(page) modify page kunmap_atomic(page) However, nothing has flushed the kernel cache view of the page before the kunmap. We need a lightweight API to do this, so this new API would specifically be for flushing the kernel cache view of a user page which the kernel has modified. The driver would need to add flush_kernel_dcache_page(page) before the final kunmap. Signed-off-by: James Bottomley Cc: Russell King Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cachetlb.txt | 12 ++++++++++++ include/linux/highmem.h | 6 ++++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/Documentation/cachetlb.txt b/Documentation/cachetlb.txt index 1f312a9893d9..53245c429f7d 100644 --- a/Documentation/cachetlb.txt +++ b/Documentation/cachetlb.txt @@ -371,6 +371,18 @@ maps this page at its virtual address. architectures). For incoherent architectures, it should flush the cache of the page at vmaddr in the current user process. + void flush_kernel_dcache_page(struct page *page) + When the kernel needs to modify a user page is has obtained + with kmap, it calls this function after all modifications are + complete (but before kunmapping it) to bring the underlying + page up to date. It is assumed here that the user has no + incoherent cached copies (i.e. the original page was obtained + from a mechanism like get_user_pages()). The default + implementation is a nop and should remain so on all coherent + architectures. On incoherent architectures, this should flush + the kernel cache for page (using page_address(page)). + + void flush_icache_range(unsigned long start, unsigned long end) When the kernel stores into addresses that it will execute out of (eg when loading modules), this function is called. diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 7bd2593dbef9..892c4ea1b425 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -13,6 +13,12 @@ static inline void flush_anon_page(struct page *page, unsigned long vmaddr) } #endif +#ifndef ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE +static inline void flush_kernel_dcache_page(struct page *page) +{ +} +#endif + #ifdef CONFIG_HIGHMEM #include -- cgit v1.2.3 From 3ed3bce846abc7ef460104b461cac793e41afe5e Mon Sep 17 00:00:00 2001 From: Matt Domsch Date: Sun, 26 Mar 2006 01:37:03 -0800 Subject: [PATCH] ia64: use i386 dmi_scan.c Enable DMI table parsing on ia64. Andi Kleen has a patch in his x86_64 tree which enables the use of i386 dmi_scan.c on x86_64. dmi_scan.c functions are being used by the drivers/char/ipmi/ipmi_si_intf.c driver for autodetecting the ports or memory spaces where the IPMI controllers may be found. This patch adds equivalent changes for ia64 as to what is in the x86_64 tree. In addition, I reworked the DMI detection, such that on EFI-capable systems, it uses the efi.smbios pointer to find the table, rather than brute-force searching from 0xF0000. On non-EFI systems, it continues the brute-force search. My test system, an Intel S870BN4 'Tiger4', aka Dell PowerEdge 7250, with latest BIOS, does not list the IPMI controller in the ACPI namespace, nor does it have an ACPI SPMI table. Also note, currently shipping Dell x8xx EM64T servers don't have these either, so DMI is the only method for obtaining the address of the IPMI controller. Signed-off-by: Matt Domsch Acked-by: "Luck, Tony" Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/kernel/dmi_scan.c | 90 +++++++++++++++++++++++++++++---------------- arch/ia64/Kconfig | 4 ++ arch/ia64/kernel/Makefile | 3 +- arch/ia64/kernel/setup.c | 8 ++++ include/asm-ia64/dmi.h | 6 +++ include/asm-ia64/io.h | 5 +++ 6 files changed, 83 insertions(+), 33 deletions(-) create mode 100644 include/asm-ia64/dmi.h (limited to 'include') diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c index ebc8dc116c43..bfecbd46f22a 100644 --- a/arch/i386/kernel/dmi_scan.c +++ b/arch/i386/kernel/dmi_scan.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -185,47 +186,72 @@ static void __init dmi_decode(struct dmi_header *dm) } } -void __init dmi_scan_machine(void) +static int __init dmi_present(char __iomem *p) { u8 buf[15]; - char __iomem *p, *q; + memcpy_fromio(buf, p, 15); + if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) { + u16 num = (buf[13] << 8) | buf[12]; + u16 len = (buf[7] << 8) | buf[6]; + u32 base = (buf[11] << 24) | (buf[10] << 16) | + (buf[9] << 8) | buf[8]; - /* - * no iounmap() for that ioremap(); it would be a no-op, but it's - * so early in setup that sucker gets confused into doing what - * it shouldn't if we actually call it. - */ - p = ioremap(0xF0000, 0x10000); - if (p == NULL) - goto out; - - for (q = p; q < p + 0x10000; q += 16) { - memcpy_fromio(buf, q, 15); - if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) { - u16 num = (buf[13] << 8) | buf[12]; - u16 len = (buf[7] << 8) | buf[6]; - u32 base = (buf[11] << 24) | (buf[10] << 16) | - (buf[9] << 8) | buf[8]; - - /* - * DMI version 0.0 means that the real version is taken from - * the SMBIOS version, which we don't know at this point. - */ - if (buf[14] != 0) - printk(KERN_INFO "DMI %d.%d present.\n", - buf[14] >> 4, buf[14] & 0xF); - else - printk(KERN_INFO "DMI present.\n"); + /* + * DMI version 0.0 means that the real version is taken from + * the SMBIOS version, which we don't know at this point. + */ + if (buf[14] != 0) + printk(KERN_INFO "DMI %d.%d present.\n", + buf[14] >> 4, buf[14] & 0xF); + else + printk(KERN_INFO "DMI present.\n"); + if (dmi_table(base,len, num, dmi_decode) == 0) + return 0; + } + return 1; +} - if (dmi_table(base,len, num, dmi_decode) == 0) +void __init dmi_scan_machine(void) +{ + char __iomem *p, *q; + int rc; + + if (efi_enabled) { + if (!efi.smbios) + goto out; + + /* This is called as a core_initcall() because it isn't + * needed during early boot. This also means we can + * iounmap the space when we're done with it. + */ + p = dmi_ioremap((unsigned long)efi.smbios, 0x10000); + if (p == NULL) + goto out; + + rc = dmi_present(p + 0x10); /* offset of _DMI_ string */ + iounmap(p); + if (!rc) + return; + } + else { + /* + * no iounmap() for that ioremap(); it would be a no-op, but + * it's so early in setup that sucker gets confused into doing + * what it shouldn't if we actually call it. + */ + p = dmi_ioremap(0xF0000, 0x10000); + if (p == NULL) + goto out; + + for (q = p; q < p + 0x10000; q += 16) { + rc = dmi_present(q); + if (!rc) return; } } - -out: printk(KERN_INFO "DMI not present or invalid.\n"); + out: printk(KERN_INFO "DMI not present or invalid.\n"); } - /** * dmi_check_system - check system DMI data * @list: array of dmi_system_id structures to match against diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index 10b6b9e7716b..d790a6d90261 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -42,6 +42,10 @@ config TIME_INTERPOLATION bool default y +config DMI + bool + default y + config EFI bool default y diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index 09a0dbc17fb6..59e871dae742 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile @@ -7,7 +7,7 @@ extra-y := head.o init_task.o vmlinux.lds obj-y := acpi.o entry.o efi.o efi_stub.o gate-data.o fsys.o ia64_ksyms.o irq.o irq_ia64.o \ irq_lsapic.o ivt.o machvec.o pal.o patch.o process.o perfmon.o ptrace.o sal.o \ salinfo.o semaphore.o setup.o signal.o sys_ia64.o time.o traps.o unaligned.o \ - unwind.o mca.o mca_asm.o topology.o + unwind.o mca.o mca_asm.o topology.o dmi_scan.o obj-$(CONFIG_IA64_BRL_EMU) += brl_emu.o obj-$(CONFIG_IA64_GENERIC) += acpi-ext.o @@ -30,6 +30,7 @@ obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o obj-$(CONFIG_KPROBES) += kprobes.o jprobes.o obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o mca_recovery-y += mca_drv.o mca_drv_asm.o +dmi_scan-y += ../../i386/kernel/dmi_scan.o # The gate DSO image is built using a special linker script. targets += gate.so gate-syms.o diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index eb388e271b2b..a4421a66ea5b 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -887,3 +888,10 @@ check_bugs (void) ia64_patch_mckinley_e9((unsigned long) __start___mckinley_e9_bundles, (unsigned long) __end___mckinley_e9_bundles); } + +static int __init run_dmi_scan(void) +{ + dmi_scan_machine(); + return 0; +} +core_initcall(run_dmi_scan); diff --git a/include/asm-ia64/dmi.h b/include/asm-ia64/dmi.h new file mode 100644 index 000000000000..f3efaa229525 --- /dev/null +++ b/include/asm-ia64/dmi.h @@ -0,0 +1,6 @@ +#ifndef _ASM_DMI_H +#define _ASM_DMI_H 1 + +#include + +#endif diff --git a/include/asm-ia64/io.h b/include/asm-ia64/io.h index b64fdb985494..0d9bcc36f2a9 100644 --- a/include/asm-ia64/io.h +++ b/include/asm-ia64/io.h @@ -435,6 +435,11 @@ iounmap (volatile void __iomem *addr) #define ioremap_nocache(o,s) ioremap(o,s) +/* Use normal IO mappings for DMI */ +#define dmi_ioremap ioremap +#define dmi_iounmap(x,l) iounmap(x) +#define dmi_alloc(l) kmalloc(l, GFP_ATOMIC) + # ifdef __KERNEL__ /* -- cgit v1.2.3 From 136939a2b5aa4302281215745ccd567e1df2e8d4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sun, 26 Mar 2006 01:37:05 -0800 Subject: [PATCH] EFI, /dev/mem: simplify efi_mem_attribute_range() Pass the size, not a pointer to the size, to efi_mem_attribute_range(). This function validates memory regions for the /dev/mem read/write/mmap paths. The pointer allows arches to reduce the size of the range, but I think that's unnecessary complexity. Simplifying it will let me use efi_mem_attribute_range() to improve the ia64 ioremap() implementation. Signed-off-by: Bjorn Helgaas Cc: Matt Domsch Cc: "Tolentino, Matthew E" Cc: "Brown, Len" Cc: Andi Kleen Acked-by: "Luck, Tony" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/kernel/efi.c | 41 ++++++++++++++++++++--------------------- drivers/char/mem.c | 18 ++++++------------ include/asm-ia64/io.h | 4 ++-- include/linux/efi.h | 2 ++ 4 files changed, 30 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 9990320b6f9a..2993748c13df 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -677,27 +677,34 @@ EXPORT_SYMBOL(efi_mem_attributes); /* * Determines whether the memory at phys_addr supports the desired * attribute (WB, UC, etc). If this returns 1, the caller can safely - * access *size bytes at phys_addr with the specified attribute. + * access size bytes at phys_addr with the specified attribute. */ -static int -efi_mem_attribute_range (unsigned long phys_addr, unsigned long *size, u64 attr) +int +efi_mem_attribute_range (unsigned long phys_addr, unsigned long size, u64 attr) { + unsigned long end = phys_addr + size; efi_memory_desc_t *md = efi_memory_descriptor(phys_addr); - unsigned long md_end; - if (!md || (md->attribute & attr) != attr) + /* + * Some firmware doesn't report MMIO regions in the EFI memory + * map. The Intel BigSur (a.k.a. HP i2000) has this problem. + * On those platforms, we have to assume UC is valid everywhere. + */ + if (!md || (md->attribute & attr) != attr) { + if (attr == EFI_MEMORY_UC && !efi_memmap_has_mmio()) + return 1; return 0; + } do { - md_end = efi_md_end(md); - if (phys_addr + *size <= md_end) + unsigned long md_end = efi_md_end(md); + + if (end <= md_end) return 1; md = efi_memory_descriptor(md_end); - if (!md || (md->attribute & attr) != attr) { - *size = md_end - phys_addr; - return 1; - } + if (!md || (md->attribute & attr) != attr) + return 0; } while (md); return 0; } @@ -708,7 +715,7 @@ efi_mem_attribute_range (unsigned long phys_addr, unsigned long *size, u64 attr) * control access size. */ int -valid_phys_addr_range (unsigned long phys_addr, unsigned long *size) +valid_phys_addr_range (unsigned long phys_addr, unsigned long size) { return efi_mem_attribute_range(phys_addr, size, EFI_MEMORY_WB); } @@ -723,7 +730,7 @@ valid_phys_addr_range (unsigned long phys_addr, unsigned long *size) * because that doesn't appear in the boot-time EFI memory map. */ int -valid_mmap_phys_addr_range (unsigned long phys_addr, unsigned long *size) +valid_mmap_phys_addr_range (unsigned long phys_addr, unsigned long size) { if (efi_mem_attribute_range(phys_addr, size, EFI_MEMORY_WB)) return 1; @@ -731,14 +738,6 @@ valid_mmap_phys_addr_range (unsigned long phys_addr, unsigned long *size) if (efi_mem_attribute_range(phys_addr, size, EFI_MEMORY_UC)) return 1; - /* - * Some firmware doesn't report MMIO regions in the EFI memory map. - * The Intel BigSur (a.k.a. HP i2000) has this problem. In this - * case, we can't use the EFI memory map to validate mmap requests. - */ - if (!efi_memmap_has_mmio()) - return 1; - return 0; } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 26d0116b48d4..5245ba1649ed 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -88,21 +88,15 @@ static inline int uncached_access(struct file *file, unsigned long addr) } #ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE -static inline int valid_phys_addr_range(unsigned long addr, size_t *count) +static inline int valid_phys_addr_range(unsigned long addr, size_t count) { - unsigned long end_mem; - - end_mem = __pa(high_memory); - if (addr >= end_mem) + if (addr + count > __pa(high_memory)) return 0; - if (*count > end_mem - addr) - *count = end_mem - addr; - return 1; } -static inline int valid_mmap_phys_addr_range(unsigned long addr, size_t *size) +static inline int valid_mmap_phys_addr_range(unsigned long addr, size_t size) { return 1; } @@ -119,7 +113,7 @@ static ssize_t read_mem(struct file * file, char __user * buf, ssize_t read, sz; char *ptr; - if (!valid_phys_addr_range(p, &count)) + if (!valid_phys_addr_range(p, count)) return -EFAULT; read = 0; #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED @@ -177,7 +171,7 @@ static ssize_t write_mem(struct file * file, const char __user * buf, unsigned long copied; void *ptr; - if (!valid_phys_addr_range(p, &count)) + if (!valid_phys_addr_range(p, count)) return -EFAULT; written = 0; @@ -249,7 +243,7 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma) { size_t size = vma->vm_end - vma->vm_start; - if (!valid_mmap_phys_addr_range(vma->vm_pgoff << PAGE_SHIFT, &size)) + if (!valid_mmap_phys_addr_range(vma->vm_pgoff << PAGE_SHIFT, size)) return -EINVAL; vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, diff --git a/include/asm-ia64/io.h b/include/asm-ia64/io.h index 0d9bcc36f2a9..acba019b08e9 100644 --- a/include/asm-ia64/io.h +++ b/include/asm-ia64/io.h @@ -88,8 +88,8 @@ phys_to_virt (unsigned long address) } #define ARCH_HAS_VALID_PHYS_ADDR_RANGE -extern int valid_phys_addr_range (unsigned long addr, size_t *count); /* efi.c */ -extern int valid_mmap_phys_addr_range (unsigned long addr, size_t *count); +extern int valid_phys_addr_range (unsigned long addr, size_t count); /* efi.c */ +extern int valid_mmap_phys_addr_range (unsigned long addr, size_t count); /* * The following two macros are deprecated and scheduled for removal. diff --git a/include/linux/efi.h b/include/linux/efi.h index c7c5dd316182..d15725470aa4 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -292,6 +292,8 @@ extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if pos extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); +extern int efi_mem_attribute_range (unsigned long phys_addr, unsigned long size, + u64 attr); extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource); -- cgit v1.2.3 From e9b0a0712148abe96ff717a2b9f8dab1d433e0d5 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sun, 26 Mar 2006 01:37:06 -0800 Subject: [PATCH] ia64: ioremap: check EFI for valid memory attributes Check the EFI memory map so we can use the correct memory attributes for ioremap(). Previously, we always used uncacheable access, which blows up on some machines for regular system memory. Signed-off-by: Bjorn Helgaas Cc: Matt Domsch Cc: "Tolentino, Matthew E" Cc: "Brown, Len" Cc: Andi Kleen Acked-by: "Luck, Tony" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/mm/Makefile | 2 +- arch/ia64/mm/ioremap.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/asm-ia64/io.h | 15 ++------------- 3 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 arch/ia64/mm/ioremap.c (limited to 'include') diff --git a/arch/ia64/mm/Makefile b/arch/ia64/mm/Makefile index d78d20f0a0f0..bb0a01a81878 100644 --- a/arch/ia64/mm/Makefile +++ b/arch/ia64/mm/Makefile @@ -2,7 +2,7 @@ # Makefile for the ia64-specific parts of the memory manager. # -obj-y := init.o fault.o tlb.o extable.o +obj-y := init.o fault.o tlb.o extable.o ioremap.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/ia64/mm/ioremap.c b/arch/ia64/mm/ioremap.c new file mode 100644 index 000000000000..62328621f99c --- /dev/null +++ b/arch/ia64/mm/ioremap.c @@ -0,0 +1,43 @@ +/* + * (c) Copyright 2006 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas + * + * 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 + +static inline void __iomem * +__ioremap (unsigned long offset, unsigned long size) +{ + return (void __iomem *) (__IA64_UNCACHED_OFFSET | offset); +} + +void __iomem * +ioremap (unsigned long offset, unsigned long size) +{ + if (efi_mem_attribute_range(offset, size, EFI_MEMORY_UC)) + return __ioremap(offset, size); + + if (efi_mem_attribute_range(offset, size, EFI_MEMORY_WB)) + return phys_to_virt(offset); + + /* + * Someday this should check ACPI resources so we + * can do the right thing for hot-plugged regions. + */ + return __ioremap(offset, size); +} +EXPORT_SYMBOL(ioremap); + +void __iomem * +ioremap_nocache (unsigned long offset, unsigned long size) +{ + return __ioremap(offset, size); +} +EXPORT_SYMBOL(ioremap_nocache); diff --git a/include/asm-ia64/io.h b/include/asm-ia64/io.h index acba019b08e9..c2e3742108bb 100644 --- a/include/asm-ia64/io.h +++ b/include/asm-ia64/io.h @@ -416,25 +416,14 @@ __writeq (unsigned long val, volatile void __iomem *addr) # define outl_p outl #endif -/* - * An "address" in IO memory space is not clearly either an integer or a pointer. We will - * accept both, thus the casts. - * - * On ia-64, we access the physical I/O memory space through the uncached kernel region. - */ -static inline void __iomem * -ioremap (unsigned long offset, unsigned long size) -{ - return (void __iomem *) (__IA64_UNCACHED_OFFSET | (offset)); -} +extern void __iomem * ioremap(unsigned long offset, unsigned long size); +extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size); static inline void iounmap (volatile void __iomem *addr) { } -#define ioremap_nocache(o,s) ioremap(o,s) - /* Use normal IO mappings for DMI */ #define dmi_ioremap ioremap #define dmi_iounmap(x,l) iounmap(x) -- cgit v1.2.3 From b2c99e3c70d77fb194df5aa1642030080d28ea48 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sun, 26 Mar 2006 01:37:08 -0800 Subject: [PATCH] EFI: keep physical table addresses in efi structure Almost all users of the table addresses from the EFI system table want physical addresses. So rather than doing the pa->va->pa conversion, just keep physical addresses in struct efi. This fixes a DMI bug: the efi structure contained the physical SMBIOS address on x86 but the virtual address on ia64, so dmi_scan_machine() used ioremap() on a virtual address on ia64. This is essentially the same as an earlier patch by Matt Tolentino: http://marc.theaimsgroup.com/?l=linux-kernel&m=112130292316281&w=2 except that this changes all table addresses, not just ACPI addresses. Matt's original patch was backed out because it caused MCAs on HP sx1000 systems. That problem is resolved by the ioremap() attribute checking added for ia64. Signed-off-by: Bjorn Helgaas Cc: Matt Domsch Cc: "Tolentino, Matthew E" Cc: "Brown, Len" Cc: Andi Kleen Acked-by: "Luck, Tony" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/kernel/acpi/boot.c | 8 ++++---- arch/i386/kernel/dmi_scan.c | 4 ++-- arch/i386/kernel/efi.c | 21 +++++++++++++++------ arch/ia64/kernel/acpi.c | 6 +++--- arch/ia64/kernel/efi.c | 21 +++++++++++++++------ arch/ia64/kernel/setup.c | 2 +- arch/ia64/sn/kernel/setup.c | 5 +++-- drivers/acpi/osl.c | 10 ++++------ drivers/firmware/efivars.c | 28 ++++++++++++++-------------- drivers/firmware/pcdp.c | 19 ++++++++++++------- include/asm-ia64/sn/sn_sal.h | 2 +- include/linux/efi.h | 18 ++++++++++-------- 12 files changed, 84 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c index f1a21945963d..033066176b3e 100644 --- a/arch/i386/kernel/acpi/boot.c +++ b/arch/i386/kernel/acpi/boot.c @@ -668,10 +668,10 @@ unsigned long __init acpi_find_rsdp(void) unsigned long rsdp_phys = 0; if (efi_enabled) { - if (efi.acpi20) - return __pa(efi.acpi20); - else if (efi.acpi) - return __pa(efi.acpi); + if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) + return efi.acpi20; + else if (efi.acpi != EFI_INVALID_TABLE_ADDR) + return efi.acpi; } /* * Scan memory looking for the RSDP signature. First search EBDA (low diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c index c032f9e06bb6..170d4c9f9bc3 100644 --- a/arch/i386/kernel/dmi_scan.c +++ b/arch/i386/kernel/dmi_scan.c @@ -217,14 +217,14 @@ void __init dmi_scan_machine(void) int rc; if (efi_enabled) { - if (!efi.smbios) + if (efi.smbios == EFI_INVALID_TABLE_ADDR) goto out; /* This is called as a core_initcall() because it isn't * needed during early boot. This also means we can * iounmap the space when we're done with it. */ - p = dmi_ioremap((unsigned long)efi.smbios, 32); + p = dmi_ioremap(efi.smbios, 32); if (p == NULL) goto out; diff --git a/arch/i386/kernel/efi.c b/arch/i386/kernel/efi.c index 7ec6cfa01fb3..c224c2aebbab 100644 --- a/arch/i386/kernel/efi.c +++ b/arch/i386/kernel/efi.c @@ -381,29 +381,38 @@ void __init efi_init(void) if (config_tables == NULL) printk(KERN_ERR PFX "Could not map EFI Configuration Table!\n"); + efi.mps = EFI_INVALID_TABLE_ADDR; + efi.acpi = EFI_INVALID_TABLE_ADDR; + efi.acpi20 = EFI_INVALID_TABLE_ADDR; + efi.smbios = EFI_INVALID_TABLE_ADDR; + efi.sal_systab = EFI_INVALID_TABLE_ADDR; + efi.boot_info = EFI_INVALID_TABLE_ADDR; + efi.hcdp = EFI_INVALID_TABLE_ADDR; + efi.uga = EFI_INVALID_TABLE_ADDR; + for (i = 0; i < num_config_tables; i++) { if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { - efi.mps = (void *)config_tables[i].table; + efi.mps = config_tables[i].table; printk(KERN_INFO " MPS=0x%lx ", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, ACPI_20_TABLE_GUID) == 0) { - efi.acpi20 = __va(config_tables[i].table); + efi.acpi20 = config_tables[i].table; printk(KERN_INFO " ACPI 2.0=0x%lx ", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, ACPI_TABLE_GUID) == 0) { - efi.acpi = __va(config_tables[i].table); + efi.acpi = config_tables[i].table; printk(KERN_INFO " ACPI=0x%lx ", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { - efi.smbios = (void *) config_tables[i].table; + efi.smbios = config_tables[i].table; printk(KERN_INFO " SMBIOS=0x%lx ", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { - efi.hcdp = (void *)config_tables[i].table; + efi.hcdp = config_tables[i].table; printk(KERN_INFO " HCDP=0x%lx ", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, UGA_IO_PROTOCOL_GUID) == 0) { - efi.uga = (void *)config_tables[i].table; + efi.uga = config_tables[i].table; printk(KERN_INFO " UGA=0x%lx ", config_tables[i].table); } } diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index a4e218ce2edb..58c93a30348c 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -651,9 +651,9 @@ unsigned long __init acpi_find_rsdp(void) { unsigned long rsdp_phys = 0; - if (efi.acpi20) - rsdp_phys = __pa(efi.acpi20); - else if (efi.acpi) + if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) + rsdp_phys = efi.acpi20; + else if (efi.acpi != EFI_INVALID_TABLE_ADDR) printk(KERN_WARNING PREFIX "v1.0/r0.71 tables no longer supported\n"); return rsdp_phys; diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 2993748c13df..12cfedce73b1 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -458,24 +458,33 @@ efi_init (void) printk(KERN_INFO "EFI v%u.%.02u by %s:", efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); + efi.mps = EFI_INVALID_TABLE_ADDR; + efi.acpi = EFI_INVALID_TABLE_ADDR; + efi.acpi20 = EFI_INVALID_TABLE_ADDR; + efi.smbios = EFI_INVALID_TABLE_ADDR; + efi.sal_systab = EFI_INVALID_TABLE_ADDR; + efi.boot_info = EFI_INVALID_TABLE_ADDR; + efi.hcdp = EFI_INVALID_TABLE_ADDR; + efi.uga = EFI_INVALID_TABLE_ADDR; + for (i = 0; i < (int) efi.systab->nr_tables; i++) { if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { - efi.mps = __va(config_tables[i].table); + efi.mps = config_tables[i].table; printk(" MPS=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, ACPI_20_TABLE_GUID) == 0) { - efi.acpi20 = __va(config_tables[i].table); + efi.acpi20 = config_tables[i].table; printk(" ACPI 2.0=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, ACPI_TABLE_GUID) == 0) { - efi.acpi = __va(config_tables[i].table); + efi.acpi = config_tables[i].table; printk(" ACPI=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { - efi.smbios = __va(config_tables[i].table); + efi.smbios = config_tables[i].table; printk(" SMBIOS=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, SAL_SYSTEM_TABLE_GUID) == 0) { - efi.sal_systab = __va(config_tables[i].table); + efi.sal_systab = config_tables[i].table; printk(" SALsystab=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { - efi.hcdp = __va(config_tables[i].table); + efi.hcdp = config_tables[i].table; printk(" HCDP=0x%lx", config_tables[i].table); } } diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index a4421a66ea5b..e4dfda1eb7dd 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -434,7 +434,7 @@ setup_arch (char **cmdline_p) find_memory(); /* process SAL system table: */ - ia64_sal_init(efi.sal_systab); + ia64_sal_init(__va(efi.sal_systab)); ia64_setup_printk_clock(); diff --git a/arch/ia64/sn/kernel/setup.c b/arch/ia64/sn/kernel/setup.c index 8b6d5c844708..30988dfbddff 100644 --- a/arch/ia64/sn/kernel/setup.c +++ b/arch/ia64/sn/kernel/setup.c @@ -327,10 +327,11 @@ sn_scan_pcdp(void) struct pcdp_interface_pci if_pci; extern struct efi efi; - pcdp = efi.hcdp; - if (! pcdp) + if (efi.hcdp == EFI_INVALID_TABLE_ADDR) return; /* no hcdp/pcdp table */ + pcdp = __va(efi.hcdp); + if (pcdp->rev < 3) return; /* only support PCDP (rev >= 3) */ diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index ac5bbaedac1b..fc8a3bce6cbb 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -156,12 +156,10 @@ acpi_status acpi_os_get_root_pointer(u32 flags, struct acpi_pointer *addr) { if (efi_enabled) { addr->pointer_type = ACPI_PHYSICAL_POINTER; - if (efi.acpi20) - addr->pointer.physical = - (acpi_physical_address) virt_to_phys(efi.acpi20); - else if (efi.acpi) - addr->pointer.physical = - (acpi_physical_address) virt_to_phys(efi.acpi); + if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) + addr->pointer.physical = efi.acpi20; + else if (efi.acpi != EFI_INVALID_TABLE_ADDR) + addr->pointer.physical = efi.acpi; else { printk(KERN_ERR PREFIX "System description tables not found\n"); diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 343379f23a53..9b7e4d52ffd4 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -568,20 +568,20 @@ systab_read(struct subsystem *entry, char *buf) if (!entry || !buf) return -EINVAL; - if (efi.mps) - str += sprintf(str, "MPS=0x%lx\n", __pa(efi.mps)); - if (efi.acpi20) - str += sprintf(str, "ACPI20=0x%lx\n", __pa(efi.acpi20)); - if (efi.acpi) - str += sprintf(str, "ACPI=0x%lx\n", __pa(efi.acpi)); - if (efi.smbios) - str += sprintf(str, "SMBIOS=0x%lx\n", __pa(efi.smbios)); - if (efi.hcdp) - str += sprintf(str, "HCDP=0x%lx\n", __pa(efi.hcdp)); - if (efi.boot_info) - str += sprintf(str, "BOOTINFO=0x%lx\n", __pa(efi.boot_info)); - if (efi.uga) - str += sprintf(str, "UGA=0x%lx\n", __pa(efi.uga)); + if (efi.mps != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "MPS=0x%lx\n", efi.mps); + if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); + if (efi.acpi != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); + if (efi.smbios != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); + if (efi.hcdp != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); + if (efi.boot_info != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info); + if (efi.uga != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "UGA=0x%lx\n", efi.uga); return str - buf; } diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c index ae1fb45dbb40..c37baf9448bc 100644 --- a/drivers/firmware/pcdp.c +++ b/drivers/firmware/pcdp.c @@ -89,19 +89,20 @@ efi_setup_pcdp_console(char *cmdline) struct pcdp_uart *uart; struct pcdp_device *dev, *end; int i, serial = 0; + int rc = -ENODEV; - pcdp = efi.hcdp; - if (!pcdp) + if (efi.hcdp == EFI_INVALID_TABLE_ADDR) return -ENODEV; - printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, __pa(pcdp)); + pcdp = ioremap(efi.hcdp, 4096); + printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, efi.hcdp); if (strstr(cmdline, "console=hcdp")) { if (pcdp->rev < 3) serial = 1; } else if (strstr(cmdline, "console=")) { printk(KERN_INFO "Explicit \"console=\"; ignoring PCDP\n"); - return -ENODEV; + goto out; } if (pcdp->rev < 3 && efi_uart_console_only()) @@ -110,7 +111,8 @@ efi_setup_pcdp_console(char *cmdline) for (i = 0, uart = pcdp->uart; i < pcdp->num_uarts; i++, uart++) { if (uart->flags & PCDP_UART_PRIMARY_CONSOLE || serial) { if (uart->type == PCDP_CONSOLE_UART) { - return setup_serial_console(uart); + rc = setup_serial_console(uart); + goto out; } } } @@ -121,10 +123,13 @@ efi_setup_pcdp_console(char *cmdline) dev = (struct pcdp_device *) ((u8 *) dev + dev->length)) { if (dev->flags & PCDP_PRIMARY_CONSOLE) { if (dev->type == PCDP_CONSOLE_VGA) { - return setup_vga_console(dev); + rc = setup_vga_console(dev); + goto out; } } } - return -ENODEV; +out: + iounmap(pcdp); + return rc; } diff --git a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h index 244449df7411..bf4cc867a698 100644 --- a/include/asm-ia64/sn/sn_sal.h +++ b/include/asm-ia64/sn/sn_sal.h @@ -159,7 +159,7 @@ static inline u32 sn_sal_rev(void) { - struct ia64_sal_systab *systab = efi.sal_systab; + struct ia64_sal_systab *systab = __va(efi.sal_systab); return (u32)(systab->sal_b_rev_major << 8 | systab->sal_b_rev_minor); } diff --git a/include/linux/efi.h b/include/linux/efi.h index d15725470aa4..e203613d3aec 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -240,19 +240,21 @@ struct efi_memory_map { unsigned long desc_size; }; +#define EFI_INVALID_TABLE_ADDR (~0UL) + /* * All runtime access to EFI goes through this structure: */ extern struct efi { efi_system_table_t *systab; /* EFI system table */ - void *mps; /* MPS table */ - void *acpi; /* ACPI table (IA64 ext 0.71) */ - void *acpi20; /* ACPI table (ACPI 2.0) */ - void *smbios; /* SM BIOS table */ - void *sal_systab; /* SAL system table */ - void *boot_info; /* boot info table */ - void *hcdp; /* HCDP table */ - void *uga; /* UGA table */ + unsigned long mps; /* MPS table */ + unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ + unsigned long acpi20; /* ACPI table (ACPI 2.0) */ + unsigned long smbios; /* SM BIOS table */ + unsigned long sal_systab; /* SAL system table */ + unsigned long boot_info; /* boot info table */ + unsigned long hcdp; /* HCDP table */ + unsigned long uga; /* UGA table */ efi_get_time_t *get_time; efi_set_time_t *set_time; efi_get_wakeup_time_t *get_wakeup_time; -- cgit v1.2.3 From 3978d7179d3849848df8a37dd0a5acc20bcb8750 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 26 Mar 2006 01:37:17 -0800 Subject: [PATCH] Make address_space_operations->sync_page return void The only user ignores the return value, and the only instanace (block_sync_page) always returns 0... Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/buffer.c | 3 +-- fs/cifs/file.c | 6 ++++-- include/linux/buffer_head.h | 2 +- include/linux/fs.h | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/fs/buffer.c b/fs/buffer.c index 3b3ab5281920..0b9456fd074f 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -3007,7 +3007,7 @@ out: } EXPORT_SYMBOL(try_to_free_buffers); -int block_sync_page(struct page *page) +void block_sync_page(struct page *page) { struct address_space *mapping; @@ -3015,7 +3015,6 @@ int block_sync_page(struct page *page) mapping = page_mapping(page); if (mapping) blk_run_backing_dev(mapping->backing_dev_info, page); - return 0; } /* diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 165d67426381..fb49aef1f2ec 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1339,7 +1339,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) return rc; } -/* static int cifs_sync_page(struct page *page) +/* static void cifs_sync_page(struct page *page) { struct address_space *mapping; struct inode *inode; @@ -1353,16 +1353,18 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) return 0; inode = mapping->host; if (!inode) - return 0; */ + return; */ /* fill in rpages then result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */ /* cFYI(1, ("rpages is %d for sync page of Index %ld ", rpages, index)); +#if 0 if (rc < 0) return rc; return 0; +#endif } */ /* diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 9f159baf153f..27dd97b3fce9 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -200,7 +200,7 @@ int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*, int generic_cont_expand(struct inode *inode, loff_t size); int generic_cont_expand_simple(struct inode *inode, loff_t size); int block_commit_write(struct page *page, unsigned from, unsigned to); -int block_sync_page(struct page *); +void block_sync_page(struct page *); sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *); int generic_commit_write(struct file *, struct page *, unsigned, unsigned); int block_truncate_page(struct address_space *, loff_t, get_block_t *); diff --git a/include/linux/fs.h b/include/linux/fs.h index 5adf32b90f36..972435d4df5c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -350,7 +350,7 @@ struct writeback_control; struct address_space_operations { int (*writepage)(struct page *page, struct writeback_control *wbc); int (*readpage)(struct file *, struct page *); - int (*sync_page)(struct page *); + void (*sync_page)(struct page *); /* Write back some dirty pages from this mapping. */ int (*writepages)(struct address_space *, struct writeback_control *); -- cgit v1.2.3 From 2ff28e22bdb8727fbc7d7889807bc5a73aae56c5 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 26 Mar 2006 01:37:18 -0800 Subject: [PATCH] Make address_space_operations->invalidatepage return void The return value of this function is never used, so let's be honest and declare it as void. Some places where invalidatepage returned 0, I have inserted comments suggesting a BUG_ON. [akpm@osdl.org: JBD BUG fix] [akpm@osdl.org: rework for git-nfs] [akpm@osdl.org: don't go BUG in block_invalidate_page()] Signed-off-by: Neil Brown Acked-by: Dave Kleikamp Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/afs/file.c | 6 +++--- fs/buffer.c | 18 ++++++++---------- fs/ext3/inode.c | 4 ++-- fs/jbd/transaction.c | 13 +++++-------- fs/jfs/jfs_metapage.c | 7 +++---- fs/nfs/file.c | 3 +-- fs/reiserfs/inode.c | 8 +++++--- fs/xfs/linux-2.6/xfs_aops.c | 4 ++-- include/linux/buffer_head.h | 4 ++-- include/linux/fs.h | 2 +- include/linux/jbd.h | 2 +- 11 files changed, 33 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/fs/afs/file.c b/fs/afs/file.c index 150b19227922..7bb716887e29 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -28,7 +28,7 @@ static int afs_file_release(struct inode *inode, struct file *file); #endif static int afs_file_readpage(struct file *file, struct page *page); -static int afs_file_invalidatepage(struct page *page, unsigned long offset); +static void afs_file_invalidatepage(struct page *page, unsigned long offset); static int afs_file_releasepage(struct page *page, gfp_t gfp_flags); struct inode_operations afs_file_inode_operations = { @@ -212,7 +212,7 @@ int afs_cache_get_page_cookie(struct page *page, /* * invalidate part or all of a page */ -static int afs_file_invalidatepage(struct page *page, unsigned long offset) +static void afs_file_invalidatepage(struct page *page, unsigned long offset) { int ret = 1; @@ -238,11 +238,11 @@ static int afs_file_invalidatepage(struct page *page, unsigned long offset) if (!PageWriteback(page)) ret = page->mapping->a_ops->releasepage(page, 0); + /* possibly should BUG_ON(!ret); - neilb */ } } _leave(" = %d", ret); - return ret; } /* end afs_file_invalidatepage() */ /*****************************************************************************/ diff --git a/fs/buffer.c b/fs/buffer.c index 0b9456fd074f..f25f58096428 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1593,11 +1593,10 @@ EXPORT_SYMBOL(try_to_release_page); * point. Because the caller is about to free (and possibly reuse) those * blocks on-disk. */ -int block_invalidatepage(struct page *page, unsigned long offset) +void block_invalidatepage(struct page *page, unsigned long offset) { struct buffer_head *head, *bh, *next; unsigned int curr_off = 0; - int ret = 1; BUG_ON(!PageLocked(page)); if (!page_has_buffers(page)) @@ -1624,19 +1623,18 @@ int block_invalidatepage(struct page *page, unsigned long offset) * so real IO is not possible anymore. */ if (offset == 0) - ret = try_to_release_page(page, 0); + try_to_release_page(page, 0); out: - return ret; + return; } EXPORT_SYMBOL(block_invalidatepage); -int do_invalidatepage(struct page *page, unsigned long offset) +void do_invalidatepage(struct page *page, unsigned long offset) { - int (*invalidatepage)(struct page *, unsigned long); - invalidatepage = page->mapping->a_ops->invalidatepage; - if (invalidatepage == NULL) - invalidatepage = block_invalidatepage; - return (*invalidatepage)(page, offset); + void (*invalidatepage)(struct page *, unsigned long); + invalidatepage = page->mapping->a_ops->invalidatepage ? : + block_invalidatepage; + (*invalidatepage)(page, offset); } /* diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 2c361377e0a5..76e22c9c9c6c 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1430,7 +1430,7 @@ ext3_readpages(struct file *file, struct address_space *mapping, return mpage_readpages(mapping, pages, nr_pages, ext3_get_block); } -static int ext3_invalidatepage(struct page *page, unsigned long offset) +static void ext3_invalidatepage(struct page *page, unsigned long offset) { journal_t *journal = EXT3_JOURNAL(page->mapping->host); @@ -1440,7 +1440,7 @@ static int ext3_invalidatepage(struct page *page, unsigned long offset) if (offset == 0) ClearPageChecked(page); - return journal_invalidatepage(journal, page, offset); + journal_invalidatepage(journal, page, offset); } static int ext3_releasepage(struct page *page, gfp_t wait) diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index ada31fa272e3..c609f5034fcd 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -1873,16 +1873,15 @@ zap_buffer_unlocked: } /** - * int journal_invalidatepage() + * void journal_invalidatepage() * @journal: journal to use for flush... * @page: page to flush * @offset: length of page to invalidate. * * Reap page buffers containing data after offset in page. * - * Return non-zero if the page's buffers were successfully reaped. */ -int journal_invalidatepage(journal_t *journal, +void journal_invalidatepage(journal_t *journal, struct page *page, unsigned long offset) { @@ -1893,7 +1892,7 @@ int journal_invalidatepage(journal_t *journal, if (!PageLocked(page)) BUG(); if (!page_has_buffers(page)) - return 1; + return; /* We will potentially be playing with lists other than just the * data lists (especially for journaled data mode), so be @@ -1916,11 +1915,9 @@ int journal_invalidatepage(journal_t *journal, } while (bh != head); if (!offset) { - if (!may_free || !try_to_free_buffers(page)) - return 0; - J_ASSERT(!page_has_buffers(page)); + if (may_free && try_to_free_buffers(page)) + J_ASSERT(!page_has_buffers(page)); } - return 1; } /* diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 5fbaeaadccd3..8508043849f3 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -578,14 +578,13 @@ static int metapage_releasepage(struct page *page, gfp_t gfp_mask) return 0; } -static int metapage_invalidatepage(struct page *page, unsigned long offset) +static void metapage_invalidatepage(struct page *page, unsigned long offset) { BUG_ON(offset); - if (PageWriteback(page)) - return 0; + BUG_ON(PageWriteback(page)); - return metapage_releasepage(page, 0); + metapage_releasepage(page, 0); } struct address_space_operations jfs_metapage_aops = { diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 5263b2864a44..dee49a0cb995 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -318,10 +318,9 @@ static int nfs_commit_write(struct file *file, struct page *page, unsigned offse return status; } -static int nfs_invalidate_page(struct page *page, unsigned long offset) +static void nfs_invalidate_page(struct page *page, unsigned long offset) { /* FIXME: we really should cancel any unstarted writes on this page */ - return 1; } static int nfs_release_page(struct page *page, gfp_t gfp) diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index d60f6238c66a..62e18c19b446 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -2793,7 +2793,7 @@ static int invalidatepage_can_drop(struct inode *inode, struct buffer_head *bh) } /* clm -- taken from fs/buffer.c:block_invalidate_page */ -static int reiserfs_invalidatepage(struct page *page, unsigned long offset) +static void reiserfs_invalidatepage(struct page *page, unsigned long offset) { struct buffer_head *head, *bh, *next; struct inode *inode = page->mapping->host; @@ -2832,10 +2832,12 @@ static int reiserfs_invalidatepage(struct page *page, unsigned long offset) * The get_block cached value has been unconditionally invalidated, * so real IO is not possible anymore. */ - if (!offset && ret) + if (!offset && ret) { ret = try_to_release_page(page, 0); + /* maybe should BUG_ON(!ret); - neilb */ + } out: - return ret; + return; } static int reiserfs_set_page_dirty(struct page *page) diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 97fc056130eb..4f2476f188b0 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1442,14 +1442,14 @@ xfs_vm_readpages( return mpage_readpages(mapping, pages, nr_pages, xfs_get_block); } -STATIC int +STATIC void xfs_vm_invalidatepage( struct page *page, unsigned long offset) { xfs_page_trace(XFS_INVALIDPAGE_ENTER, page->mapping->host, page, offset); - return block_invalidatepage(page, offset); + block_invalidatepage(page, offset); } struct address_space_operations xfs_address_space_operations = { diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 27dd97b3fce9..da917ed096a3 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -189,8 +189,8 @@ extern int buffer_heads_over_limit; * address_spaces. */ int try_to_release_page(struct page * page, gfp_t gfp_mask); -int block_invalidatepage(struct page *page, unsigned long offset); -int do_invalidatepage(struct page *page, unsigned long offset); +void block_invalidatepage(struct page *page, unsigned long offset); +void do_invalidatepage(struct page *page, unsigned long offset); int block_write_full_page(struct page *page, get_block_t *get_block, struct writeback_control *wbc); int block_read_full_page(struct page*, get_block_t*); diff --git a/include/linux/fs.h b/include/linux/fs.h index 972435d4df5c..9674679525f9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -369,7 +369,7 @@ struct address_space_operations { int (*commit_write)(struct file *, struct page *, unsigned, unsigned); /* Unfortunately this kludge is needed for FIBMAP. Don't use it */ sector_t (*bmap)(struct address_space *, sector_t); - int (*invalidatepage) (struct page *, unsigned long); + void (*invalidatepage) (struct page *, unsigned long); int (*releasepage) (struct page *, gfp_t); ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov, loff_t offset, unsigned long nr_segs); diff --git a/include/linux/jbd.h b/include/linux/jbd.h index 4fc7dffd66ef..6a425e370cb3 100644 --- a/include/linux/jbd.h +++ b/include/linux/jbd.h @@ -895,7 +895,7 @@ extern int journal_dirty_metadata (handle_t *, struct buffer_head *); extern void journal_release_buffer (handle_t *, struct buffer_head *); extern int journal_forget (handle_t *, struct buffer_head *); extern void journal_sync_buffer (struct buffer_head *); -extern int journal_invalidatepage(journal_t *, +extern void journal_invalidatepage(journal_t *, struct page *, unsigned long); extern int journal_try_to_free_buffers(journal_t *, struct page *, gfp_t); extern int journal_stop(handle_t *); -- cgit v1.2.3 From 3c30b06df404c8892c225a99ecfd3f02789c0513 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Sun, 26 Mar 2006 01:37:19 -0800 Subject: [PATCH] cleanup smp_call_function UP build net/core/flow.c: In function 'flow_cache_flush': net/core/flow.c:299: warning: statement with no effect Signed-off-by: Con Kolivas Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/smp.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/smp.h b/include/linux/smp.h index d699a16b0cb2..e2fa3ab4afc5 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -82,7 +82,11 @@ void smp_prepare_boot_cpu(void); */ #define raw_smp_processor_id() 0 #define hard_smp_processor_id() 0 -#define smp_call_function(func,info,retry,wait) ({ 0; }) +static inline int up_smp_call_function(void) +{ + return 0; +} +#define smp_call_function(func,info,retry,wait) (up_smp_call_function()) #define on_each_cpu(func,info,retry,wait) \ ({ \ local_irq_disable(); \ -- cgit v1.2.3 From 50c812b2b9513e3df34eae8c30cb2c221b79b2cb Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Sun, 26 Mar 2006 01:37:21 -0800 Subject: [PATCH] ipmi: add full sysfs support Add full driver model support for the IPMI driver. It links in the proper bus and device support. It adds an "ipmi" driver interface that has each BMC discovered by the driver (as a device). These BMCs appear in the devices/platform directory. If there are multiple interfaces to the same BMC, the driver should discover this and will only have one BMC entry. The BMC entry will have pointers to each interface device that connects to it. The device information (statistics and config information) has not yet been ported over to the driver model from proc, that will come later. This work was based on work by Yani Ioannou. I basically rewrote it using that code as a guide, but he still deserves credit :). [bunk@stusta.de: make ipmi_find_bmc_guid() static] Signed-off-by: Corey Minyard Signed-off-by: Yani Ioannou Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_devintf.c | 48 +++- drivers/char/ipmi/ipmi_msghandler.c | 552 +++++++++++++++++++++++++++++++++++- drivers/char/ipmi/ipmi_poweroff.c | 2 +- drivers/char/ipmi/ipmi_si_intf.c | 114 +++++--- drivers/char/ipmi/ipmi_watchdog.c | 2 +- include/linux/ipmi.h | 3 +- include/linux/ipmi_msgdefs.h | 1 + include/linux/ipmi_smi.h | 47 ++- 8 files changed, 712 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 7c0684deea06..932feedda262 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -90,7 +90,7 @@ static unsigned int ipmi_poll(struct file *file, poll_table *wait) spin_lock_irqsave(&priv->recv_msg_lock, flags); - if (! list_empty(&(priv->recv_msgs))) + if (!list_empty(&(priv->recv_msgs))) mask |= (POLLIN | POLLRDNORM); spin_unlock_irqrestore(&priv->recv_msg_lock, flags); @@ -789,21 +789,53 @@ MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By" " interface. Other values will set the major device number" " to that value."); +/* Keep track of the devices that are registered. */ +struct ipmi_reg_list { + dev_t dev; + struct list_head link; +}; +static LIST_HEAD(reg_list); +static DEFINE_MUTEX(reg_list_mutex); + static struct class *ipmi_class; -static void ipmi_new_smi(int if_num) +static void ipmi_new_smi(int if_num, struct device *device) { dev_t dev = MKDEV(ipmi_major, if_num); + struct ipmi_reg_list *entry; devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR, "ipmidev/%d", if_num); - class_device_create(ipmi_class, NULL, dev, NULL, "ipmi%d", if_num); + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + printk(KERN_ERR "ipmi_devintf: Unable to create the" + " ipmi class device link\n"); + return; + } + entry->dev = dev; + + mutex_lock(®_list_mutex); + class_device_create(ipmi_class, NULL, dev, device, "ipmi%d", if_num); + list_add(&entry->link, ®_list); + mutex_unlock(®_list_mutex); } static void ipmi_smi_gone(int if_num) { - class_device_destroy(ipmi_class, MKDEV(ipmi_major, if_num)); + dev_t dev = MKDEV(ipmi_major, if_num); + struct ipmi_reg_list *entry; + + mutex_lock(®_list_mutex); + list_for_each_entry(entry, ®_list, link) { + if (entry->dev == dev) { + list_del(&entry->link); + kfree(entry); + break; + } + } + class_device_destroy(ipmi_class, dev); + mutex_unlock(®_list_mutex); devfs_remove("ipmidev/%d", if_num); } @@ -856,6 +888,14 @@ module_init(init_ipmi_devintf); static __exit void cleanup_ipmi(void) { + struct ipmi_reg_list *entry, *entry2; + mutex_lock(®_list_mutex); + list_for_each_entry_safe(entry, entry2, ®_list, link) { + list_del(&entry->link); + class_device_destroy(ipmi_class, entry->dev); + kfree(entry); + } + mutex_unlock(®_list_mutex); class_destroy(ipmi_class); ipmi_smi_watcher_unregister(&smi_watcher); devfs_remove(DEVICE_NAME); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index abd4c5118a1b..f553b7a86841 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -162,6 +162,28 @@ struct ipmi_proc_entry }; #endif +struct bmc_device +{ + struct platform_device *dev; + struct ipmi_device_id id; + unsigned char guid[16]; + int guid_set; + + struct kref refcount; + + /* bmc device attributes */ + struct device_attribute device_id_attr; + struct device_attribute provides_dev_sdrs_attr; + struct device_attribute revision_attr; + struct device_attribute firmware_rev_attr; + struct device_attribute version_attr; + struct device_attribute add_dev_support_attr; + struct device_attribute manufacturer_id_attr; + struct device_attribute product_id_attr; + struct device_attribute guid_attr; + struct device_attribute aux_firmware_rev_attr; +}; + #define IPMI_IPMB_NUM_SEQ 64 #define IPMI_MAX_CHANNELS 16 struct ipmi_smi @@ -178,9 +200,8 @@ struct ipmi_smi /* Used for wake ups at startup. */ wait_queue_head_t waitq; - /* The IPMI version of the BMC on the other end. */ - unsigned char version_major; - unsigned char version_minor; + struct bmc_device *bmc; + char *my_dev_name; /* This is the lower-layer's sender routine. */ struct ipmi_smi_handlers *handlers; @@ -194,6 +215,9 @@ struct ipmi_smi struct ipmi_proc_entry *proc_entries; #endif + /* Driver-model device for the system interface. */ + struct device *si_dev; + /* A table of sequence numbers for this interface. We use the sequence numbers for IPMB messages that go out of the interface to match them up with their responses. A routine @@ -312,6 +336,7 @@ struct ipmi_smi /* Events that were received with the proper format. */ unsigned int events; }; +#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev) /* Used to mark an interface entry that cannot be used but is not a * free entry, either, primarily used at creation and deletion time so @@ -320,6 +345,15 @@ struct ipmi_smi #define IPMI_INVALID_INTERFACE(i) (((i) == NULL) \ || (i == IPMI_INVALID_INTERFACE_ENTRY)) +/** + * The driver model view of the IPMI messaging driver. + */ +static struct device_driver ipmidriver = { + .name = "ipmi", + .bus = &platform_bus_type +}; +static DEFINE_MUTEX(ipmidriver_mutex); + #define MAX_IPMI_INTERFACES 4 static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES]; @@ -393,7 +427,7 @@ int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) if (IPMI_INVALID_INTERFACE(intf)) continue; spin_unlock_irqrestore(&interfaces_lock, flags); - watcher->new_smi(i); + watcher->new_smi(i, intf->si_dev); spin_lock_irqsave(&interfaces_lock, flags); } spin_unlock_irqrestore(&interfaces_lock, flags); @@ -409,14 +443,14 @@ int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) } static void -call_smi_watchers(int i) +call_smi_watchers(int i, struct device *dev) { struct ipmi_smi_watcher *w; down_read(&smi_watchers_sem); list_for_each_entry(w, &smi_watchers, link) { if (try_module_get(w->owner)) { - w->new_smi(i); + w->new_smi(i, dev); module_put(w->owner); } } @@ -844,8 +878,8 @@ void ipmi_get_version(ipmi_user_t user, unsigned char *major, unsigned char *minor) { - *major = user->intf->version_major; - *minor = user->intf->version_minor; + *major = ipmi_version_major(&user->intf->bmc->id); + *minor = ipmi_version_minor(&user->intf->bmc->id); } int ipmi_set_my_address(ipmi_user_t user, @@ -1553,7 +1587,8 @@ static int version_file_read_proc(char *page, char **start, off_t off, ipmi_smi_t intf = data; return sprintf(out, "%d.%d\n", - intf->version_major, intf->version_minor); + ipmi_version_major(&intf->bmc->id), + ipmi_version_minor(&intf->bmc->id)); } static int stat_file_read_proc(char *page, char **start, off_t off, @@ -1712,6 +1747,470 @@ static void remove_proc_entries(ipmi_smi_t smi) #endif /* CONFIG_PROC_FS */ } +static int __find_bmc_guid(struct device *dev, void *data) +{ + unsigned char *id = data; + struct bmc_device *bmc = dev_get_drvdata(dev); + return memcmp(bmc->guid, id, 16) == 0; +} + +static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv, + unsigned char *guid) +{ + struct device *dev; + + dev = driver_find_device(drv, NULL, guid, __find_bmc_guid); + if (dev) + return dev_get_drvdata(dev); + else + return NULL; +} + +struct prod_dev_id { + unsigned int product_id; + unsigned char device_id; +}; + +static int __find_bmc_prod_dev_id(struct device *dev, void *data) +{ + struct prod_dev_id *id = data; + struct bmc_device *bmc = dev_get_drvdata(dev); + + return (bmc->id.product_id == id->product_id + && bmc->id.product_id == id->product_id + && bmc->id.device_id == id->device_id); +} + +static struct bmc_device *ipmi_find_bmc_prod_dev_id( + struct device_driver *drv, + unsigned char product_id, unsigned char device_id) +{ + struct prod_dev_id id = { + .product_id = product_id, + .device_id = device_id, + }; + struct device *dev; + + dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id); + if (dev) + return dev_get_drvdata(dev); + else + return NULL; +} + +static ssize_t device_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "%u\n", bmc->id.device_id); +} + +static ssize_t provides_dev_sdrs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "%u\n", + bmc->id.device_revision && 0x80 >> 7); +} + +static ssize_t revision_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "%u\n", + bmc->id.device_revision && 0x0F); +} + +static ssize_t firmware_rev_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1, + bmc->id.firmware_revision_2); +} + +static ssize_t ipmi_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "%u.%u\n", + ipmi_version_major(&bmc->id), + ipmi_version_minor(&bmc->id)); +} + +static ssize_t add_dev_support_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "0x%02x\n", + bmc->id.additional_device_support); +} + +static ssize_t manufacturer_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id); +} + +static ssize_t product_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id); +} + +static ssize_t aux_firmware_rev_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n", + bmc->id.aux_firmware_revision[3], + bmc->id.aux_firmware_revision[2], + bmc->id.aux_firmware_revision[1], + bmc->id.aux_firmware_revision[0]); +} + +static ssize_t guid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 100, "%Lx%Lx\n", + (long long) bmc->guid[0], + (long long) bmc->guid[8]); +} + +static void +cleanup_bmc_device(struct kref *ref) +{ + struct bmc_device *bmc; + + bmc = container_of(ref, struct bmc_device, refcount); + + device_remove_file(&bmc->dev->dev, + &bmc->device_id_attr); + device_remove_file(&bmc->dev->dev, + &bmc->provides_dev_sdrs_attr); + device_remove_file(&bmc->dev->dev, + &bmc->revision_attr); + device_remove_file(&bmc->dev->dev, + &bmc->firmware_rev_attr); + device_remove_file(&bmc->dev->dev, + &bmc->version_attr); + device_remove_file(&bmc->dev->dev, + &bmc->add_dev_support_attr); + device_remove_file(&bmc->dev->dev, + &bmc->manufacturer_id_attr); + device_remove_file(&bmc->dev->dev, + &bmc->product_id_attr); + if (bmc->id.aux_firmware_revision_set) + device_remove_file(&bmc->dev->dev, + &bmc->aux_firmware_rev_attr); + if (bmc->guid_set) + device_remove_file(&bmc->dev->dev, + &bmc->guid_attr); + platform_device_unregister(bmc->dev); + kfree(bmc); +} + +static void ipmi_bmc_unregister(ipmi_smi_t intf) +{ + struct bmc_device *bmc = intf->bmc; + + sysfs_remove_link(&intf->si_dev->kobj, "bmc"); + if (intf->my_dev_name) { + sysfs_remove_link(&bmc->dev->dev.kobj, intf->my_dev_name); + kfree(intf->my_dev_name); + intf->my_dev_name = NULL; + } + + mutex_lock(&ipmidriver_mutex); + kref_put(&bmc->refcount, cleanup_bmc_device); + mutex_unlock(&ipmidriver_mutex); +} + +static int ipmi_bmc_register(ipmi_smi_t intf) +{ + int rv; + struct bmc_device *bmc = intf->bmc; + struct bmc_device *old_bmc; + int size; + char dummy[1]; + + mutex_lock(&ipmidriver_mutex); + + /* + * Try to find if there is an bmc_device struct + * representing the interfaced BMC already + */ + if (bmc->guid_set) + old_bmc = ipmi_find_bmc_guid(&ipmidriver, bmc->guid); + else + old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver, + bmc->id.product_id, + bmc->id.device_id); + + /* + * If there is already an bmc_device, free the new one, + * otherwise register the new BMC device + */ + if (old_bmc) { + kfree(bmc); + intf->bmc = old_bmc; + bmc = old_bmc; + + kref_get(&bmc->refcount); + mutex_unlock(&ipmidriver_mutex); + + printk(KERN_INFO + "ipmi: interfacing existing BMC (man_id: 0x%6.6x," + " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); + } else { + bmc->dev = platform_device_alloc("ipmi_bmc", + bmc->id.device_id); + if (! bmc->dev) { + printk(KERN_ERR + "ipmi_msghandler:" + " Unable to allocate platform device\n"); + return -ENOMEM; + } + bmc->dev->dev.driver = &ipmidriver; + dev_set_drvdata(&bmc->dev->dev, bmc); + kref_init(&bmc->refcount); + + rv = platform_device_register(bmc->dev); + mutex_unlock(&ipmidriver_mutex); + if (rv) { + printk(KERN_ERR + "ipmi_msghandler:" + " Unable to register bmc device: %d\n", + rv); + /* Don't go to out_err, you can only do that if + the device is registered already. */ + return rv; + } + + bmc->device_id_attr.attr.name = "device_id"; + bmc->device_id_attr.attr.owner = THIS_MODULE; + bmc->device_id_attr.attr.mode = S_IRUGO; + bmc->device_id_attr.show = device_id_show; + + bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs"; + bmc->provides_dev_sdrs_attr.attr.owner = THIS_MODULE; + bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO; + bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show; + + + bmc->revision_attr.attr.name = "revision"; + bmc->revision_attr.attr.owner = THIS_MODULE; + bmc->revision_attr.attr.mode = S_IRUGO; + bmc->revision_attr.show = revision_show; + + bmc->firmware_rev_attr.attr.name = "firmware_revision"; + bmc->firmware_rev_attr.attr.owner = THIS_MODULE; + bmc->firmware_rev_attr.attr.mode = S_IRUGO; + bmc->firmware_rev_attr.show = firmware_rev_show; + + bmc->version_attr.attr.name = "ipmi_version"; + bmc->version_attr.attr.owner = THIS_MODULE; + bmc->version_attr.attr.mode = S_IRUGO; + bmc->version_attr.show = ipmi_version_show; + + bmc->add_dev_support_attr.attr.name + = "additional_device_support"; + bmc->add_dev_support_attr.attr.owner = THIS_MODULE; + bmc->add_dev_support_attr.attr.mode = S_IRUGO; + bmc->add_dev_support_attr.show = add_dev_support_show; + + bmc->manufacturer_id_attr.attr.name = "manufacturer_id"; + bmc->manufacturer_id_attr.attr.owner = THIS_MODULE; + bmc->manufacturer_id_attr.attr.mode = S_IRUGO; + bmc->manufacturer_id_attr.show = manufacturer_id_show; + + bmc->product_id_attr.attr.name = "product_id"; + bmc->product_id_attr.attr.owner = THIS_MODULE; + bmc->product_id_attr.attr.mode = S_IRUGO; + bmc->product_id_attr.show = product_id_show; + + bmc->guid_attr.attr.name = "guid"; + bmc->guid_attr.attr.owner = THIS_MODULE; + bmc->guid_attr.attr.mode = S_IRUGO; + bmc->guid_attr.show = guid_show; + + bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision"; + bmc->aux_firmware_rev_attr.attr.owner = THIS_MODULE; + bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO; + bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show; + + device_create_file(&bmc->dev->dev, + &bmc->device_id_attr); + device_create_file(&bmc->dev->dev, + &bmc->provides_dev_sdrs_attr); + device_create_file(&bmc->dev->dev, + &bmc->revision_attr); + device_create_file(&bmc->dev->dev, + &bmc->firmware_rev_attr); + device_create_file(&bmc->dev->dev, + &bmc->version_attr); + device_create_file(&bmc->dev->dev, + &bmc->add_dev_support_attr); + device_create_file(&bmc->dev->dev, + &bmc->manufacturer_id_attr); + device_create_file(&bmc->dev->dev, + &bmc->product_id_attr); + if (bmc->id.aux_firmware_revision_set) + device_create_file(&bmc->dev->dev, + &bmc->aux_firmware_rev_attr); + if (bmc->guid_set) + device_create_file(&bmc->dev->dev, + &bmc->guid_attr); + + printk(KERN_INFO + "ipmi: Found new BMC (man_id: 0x%6.6x, " + " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); + } + + /* + * create symlink from system interface device to bmc device + * and back. + */ + rv = sysfs_create_link(&intf->si_dev->kobj, + &bmc->dev->dev.kobj, "bmc"); + if (rv) { + printk(KERN_ERR + "ipmi_msghandler: Unable to create bmc symlink: %d\n", + rv); + goto out_err; + } + + size = snprintf(dummy, 0, "ipmi%d", intf->intf_num); + intf->my_dev_name = kmalloc(size+1, GFP_KERNEL); + if (!intf->my_dev_name) { + rv = -ENOMEM; + printk(KERN_ERR + "ipmi_msghandler: allocate link from BMC: %d\n", + rv); + goto out_err; + } + snprintf(intf->my_dev_name, size+1, "ipmi%d", intf->intf_num); + + rv = sysfs_create_link(&bmc->dev->dev.kobj, &intf->si_dev->kobj, + intf->my_dev_name); + if (rv) { + kfree(intf->my_dev_name); + intf->my_dev_name = NULL; + printk(KERN_ERR + "ipmi_msghandler:" + " Unable to create symlink to bmc: %d\n", + rv); + goto out_err; + } + + return 0; + +out_err: + ipmi_bmc_unregister(intf); + return rv; +} + +static int +send_guid_cmd(ipmi_smi_t intf, int chan) +{ + struct kernel_ipmi_msg msg; + struct ipmi_system_interface_addr si; + + si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + si.channel = IPMI_BMC_CHANNEL; + si.lun = 0; + + msg.netfn = IPMI_NETFN_APP_REQUEST; + msg.cmd = IPMI_GET_DEVICE_GUID_CMD; + msg.data = NULL; + msg.data_len = 0; + return i_ipmi_request(NULL, + intf, + (struct ipmi_addr *) &si, + 0, + &msg, + intf, + NULL, + NULL, + 0, + intf->channels[0].address, + intf->channels[0].lun, + -1, 0); +} + +static void +guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +{ + if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) + || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) + || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD)) + /* Not for me */ + return; + + if (msg->msg.data[0] != 0) { + /* Error from getting the GUID, the BMC doesn't have one. */ + intf->bmc->guid_set = 0; + goto out; + } + + if (msg->msg.data_len < 17) { + intf->bmc->guid_set = 0; + printk(KERN_WARNING PFX + "guid_handler: The GUID response from the BMC was too" + " short, it was %d but should have been 17. Assuming" + " GUID is not available.\n", + msg->msg.data_len); + goto out; + } + + memcpy(intf->bmc->guid, msg->msg.data, 16); + intf->bmc->guid_set = 1; + out: + wake_up(&intf->waitq); +} + +static void +get_guid(ipmi_smi_t intf) +{ + int rv; + + intf->bmc->guid_set = 0x2; + intf->null_user_handler = guid_handler; + rv = send_guid_cmd(intf, 0); + if (rv) + /* Send failed, no GUID available. */ + intf->bmc->guid_set = 0; + wait_event(intf->waitq, intf->bmc->guid_set != 2); + intf->null_user_handler = NULL; +} + static int send_channel_info_cmd(ipmi_smi_t intf, int chan) { @@ -1804,8 +2303,8 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, - unsigned char version_major, - unsigned char version_minor, + struct ipmi_device_id *device_id, + struct device *si_dev, unsigned char slave_addr, ipmi_smi_t *new_intf) { @@ -1813,7 +2312,11 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, int rv; ipmi_smi_t intf; unsigned long flags; + int version_major; + int version_minor; + version_major = ipmi_version_major(device_id); + version_minor = ipmi_version_minor(device_id); /* Make sure the driver is actually initialized, this handles problems with initialization order. */ @@ -1831,10 +2334,15 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, if (!intf) return -ENOMEM; memset(intf, 0, sizeof(*intf)); + intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL); + if (!intf->bmc) { + kfree(intf); + return -ENOMEM; + } intf->intf_num = -1; kref_init(&intf->refcount); - intf->version_major = version_major; - intf->version_minor = version_minor; + intf->bmc->id = *device_id; + intf->si_dev = si_dev; for (j = 0; j < IPMI_MAX_CHANNELS; j++) { intf->channels[j].address = IPMI_BMC_SLAVE_ADDR; intf->channels[j].lun = 2; @@ -1884,6 +2392,8 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, caller before sending any messages with it. */ *new_intf = intf; + get_guid(intf); + if ((version_major > 1) || ((version_major == 1) && (version_minor >= 5))) { @@ -1898,6 +2408,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, /* Wait for the channel info to be read. */ wait_event(intf->waitq, intf->curr_channel >= IPMI_MAX_CHANNELS); + intf->null_user_handler = NULL; } else { /* Assume a single IPMB channel at zero. */ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; @@ -1907,6 +2418,8 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, if (rv == 0) rv = add_proc_entries(intf, i); + rv = ipmi_bmc_register(intf); + out: if (rv) { if (intf->proc_dir) @@ -1921,7 +2434,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, spin_lock_irqsave(&interfaces_lock, flags); ipmi_interfaces[i] = intf; spin_unlock_irqrestore(&interfaces_lock, flags); - call_smi_watchers(i); + call_smi_watchers(i, intf->si_dev); } return rv; @@ -1933,6 +2446,8 @@ int ipmi_unregister_smi(ipmi_smi_t intf) struct ipmi_smi_watcher *w; unsigned long flags; + ipmi_bmc_unregister(intf); + spin_lock_irqsave(&interfaces_lock, flags); for (i = 0; i < MAX_IPMI_INTERFACES; i++) { if (ipmi_interfaces[i] == intf) { @@ -3196,10 +3711,17 @@ static struct notifier_block panic_block = { static int ipmi_init_msghandler(void) { int i; + int rv; if (initialized) return 0; + rv = driver_register(&ipmidriver); + if (rv) { + printk(KERN_ERR PFX "Could not register IPMI driver\n"); + return rv; + } + printk(KERN_INFO "ipmi message handler version " IPMI_DRIVER_VERSION "\n"); @@ -3256,6 +3778,8 @@ static __exit void cleanup_ipmi(void) remove_proc_entry(proc_ipmi_root->name, &proc_root); #endif /* CONFIG_PROC_FS */ + driver_unregister(&ipmidriver); + initialized = 0; /* Check for buffer leaks. */ diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index e8ed26b77d4c..786a2802ca34 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -464,7 +464,7 @@ static void ipmi_poweroff_function (void) /* Wait for an IPMI interface to be installed, the first one installed will be grabbed by this code and used to perform the powerdown. */ -static void ipmi_po_new_smi(int if_num) +static void ipmi_po_new_smi(int if_num, struct device *device) { struct ipmi_system_interface_addr smi_addr; struct kernel_ipmi_msg send_msg; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index f3b3b23c5330..12f858dc9994 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -112,20 +112,13 @@ enum si_type { }; static char *si_to_str[] = { "KCS", "SMIC", "BT" }; -struct ipmi_device_id { - unsigned char device_id; - unsigned char device_revision; - unsigned char firmware_revision_1; - unsigned char firmware_revision_2; - unsigned char ipmi_version; - unsigned char additional_device_support; - unsigned char manufacturer_id[3]; - unsigned char product_id[2]; - unsigned char aux_firmware_revision[4]; -} __attribute__((packed)); - -#define ipmi_version_major(v) ((v)->ipmi_version & 0xf) -#define ipmi_version_minor(v) ((v)->ipmi_version >> 4) +#define DEVICE_NAME "ipmi_si" + +static struct device_driver ipmi_driver = +{ + .name = DEVICE_NAME, + .bus = &platform_bus_type +}; struct smi_info { @@ -208,8 +201,17 @@ struct smi_info interrupts. */ int interrupt_disabled; + /* From the get device id response... */ struct ipmi_device_id device_id; + /* Driver model stuff. */ + struct device *dev; + struct platform_device *pdev; + + /* True if we allocated the device, false if it came from + * someplace else (like PCI). */ + int dev_registered; + /* Slave address, could be reported from DMI. */ unsigned char slave_addr; @@ -987,8 +989,6 @@ static LIST_HEAD(smi_infos); static DECLARE_MUTEX(smi_infos_lock); static int smi_num; /* Used to sequence the SMIs */ -#define DEVICE_NAME "ipmi_si" - #define DEFAULT_REGSPACING 1 static int si_trydefaults = 1; @@ -1164,7 +1164,6 @@ static void port_cleanup(struct smi_info *info) release_region (addr, mapsize); } - kfree(info); } static int port_setup(struct smi_info *info) @@ -1273,7 +1272,6 @@ static void mem_cleanup(struct smi_info *info) release_mem_region(addr, mapsize); } - kfree(info); } static int mem_setup(struct smi_info *info) @@ -1858,6 +1856,8 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, if (info->irq) info->irq_setup = std_irq_setup; + info->dev = &pdev->dev; + return try_smi_init(info); } @@ -1898,11 +1898,11 @@ static struct pci_driver ipmi_pci_driver = { static int try_get_dev_id(struct smi_info *smi_info) { - unsigned char msg[2]; - unsigned char *resp; - unsigned long resp_len; - enum si_sm_result smi_result; - int rv = 0; + unsigned char msg[2]; + unsigned char *resp; + unsigned long resp_len; + enum si_sm_result smi_result; + int rv = 0; resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); if (!resp) @@ -1941,7 +1941,7 @@ static int try_get_dev_id(struct smi_info *smi_info) /* Otherwise, we got some data. */ resp_len = smi_info->handlers->get_result(smi_info->si_sm, resp, IPMI_MAX_MSG_LENGTH); - if (resp_len < 6) { + if (resp_len < 14) { /* That's odd, it should be longer. */ rv = -EINVAL; goto out; @@ -1954,8 +1954,7 @@ static int try_get_dev_id(struct smi_info *smi_info) } /* Record info from the get device id, in case we need it. */ - memcpy(&smi_info->device_id, &resp[3], - min_t(unsigned long, resp_len-3, sizeof(smi_info->device_id))); + ipmi_demangle_device_id(resp+3, resp_len-3, &smi_info->device_id); out: kfree(resp); @@ -2058,15 +2057,14 @@ static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info) #define DELL_POWEREDGE_8G_BMC_DEVICE_ID 0x20 #define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80 #define DELL_POWEREDGE_8G_BMC_IPMI_VERSION 0x51 -#define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00} +#define DELL_IANA_MFR_ID 0x0002a2 static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; - const char mfr[3]=DELL_IANA_MFR_ID; - if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr))) { + if (id->manufacturer_id == DELL_IANA_MFR_ID) { if (id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID && id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV && - id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { + id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { smi_info->oem_data_avail_handler = oem_data_avail_to_receive_msg_avail; } @@ -2138,8 +2136,7 @@ static void setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; - const char mfr[3]=DELL_IANA_MFR_ID; - if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr)) && + if (id->manufacturer_id == DELL_IANA_MFR_ID && smi_info->si_type == SI_BT) register_xaction_notifier(&dell_poweredge_bt_xaction_notifier); } @@ -2358,10 +2355,36 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->thread = kthread_run(ipmi_thread, new_smi, "kipmi%d", new_smi->intf_num); + if (!new_smi->dev) { + /* If we don't already have a device from something + * else (like PCI), then register a new one. */ + new_smi->pdev = platform_device_alloc("ipmi_si", + new_smi->intf_num); + if (rv) { + printk(KERN_ERR + "ipmi_si_intf:" + " Unable to allocate platform device\n"); + goto out_err_stop_timer; + } + new_smi->dev = &new_smi->pdev->dev; + new_smi->dev->driver = &ipmi_driver; + + rv = platform_device_register(new_smi->pdev); + if (rv) { + printk(KERN_ERR + "ipmi_si_intf:" + " Unable to register system interface device:" + " %d\n", + rv); + goto out_err_stop_timer; + } + new_smi->dev_registered = 1; + } + rv = ipmi_register_smi(&handlers, new_smi, - ipmi_version_major(&new_smi->device_id), - ipmi_version_minor(&new_smi->device_id), + &new_smi->device_id, + new_smi->dev, new_smi->slave_addr, &(new_smi->intf)); if (rv) { @@ -2425,6 +2448,11 @@ static int try_smi_init(struct smi_info *new_smi) if (new_smi->io_cleanup) new_smi->io_cleanup(new_smi); + if (new_smi->dev_registered) + platform_device_unregister(new_smi->pdev); + + kfree(new_smi); + up(&smi_infos_lock); return rv; @@ -2434,11 +2462,22 @@ static __devinit int init_ipmi_si(void) { int i; char *str; + int rv; if (initialized) return 0; initialized = 1; + /* Register the device drivers. */ + rv = driver_register(&ipmi_driver); + if (rv) { + printk(KERN_ERR + "init_ipmi_si: Unable to register driver: %d\n", + rv); + return rv; + } + + /* Parse out the si_type string into its components. */ str = si_type_str; if (*str != '\0') { @@ -2549,6 +2588,11 @@ static void __devexit cleanup_one_si(struct smi_info *to_clean) to_clean->addr_source_cleanup(to_clean); if (to_clean->io_cleanup) to_clean->io_cleanup(to_clean); + + if (to_clean->dev_registered) + platform_device_unregister(to_clean->pdev); + + kfree(to_clean); } static __exit void cleanup_ipmi_si(void) @@ -2566,6 +2610,8 @@ static __exit void cleanup_ipmi_si(void) list_for_each_entry_safe(e, tmp_e, &smi_infos, link) cleanup_one_si(e); up(&smi_infos_lock); + + driver_unregister(&ipmi_driver); } module_exit(cleanup_ipmi_si); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 1f3159eb1ede..616539310d9a 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -996,7 +996,7 @@ static struct notifier_block wdog_panic_notifier = { }; -static void ipmi_new_smi(int if_num) +static void ipmi_new_smi(int if_num, struct device *device) { ipmi_register_watchdog(if_num); } diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index d6276e60b3bf..0a84b56935c2 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -36,6 +36,7 @@ #include #include +#include /* * This file describes an interface to an IPMI driver. You have to @@ -397,7 +398,7 @@ struct ipmi_smi_watcher the watcher list. So you can add and remove users from the IPMI interface, send messages, etc., but you cannot add or remove SMI watchers or SMI interfaces. */ - void (*new_smi)(int if_num); + void (*new_smi)(int if_num, struct device *dev); void (*smi_gone)(int if_num); }; diff --git a/include/linux/ipmi_msgdefs.h b/include/linux/ipmi_msgdefs.h index 03bc64dc2ec1..22f5e2afda4f 100644 --- a/include/linux/ipmi_msgdefs.h +++ b/include/linux/ipmi_msgdefs.h @@ -47,6 +47,7 @@ #define IPMI_NETFN_APP_RESPONSE 0x07 #define IPMI_GET_DEVICE_ID_CMD 0x01 #define IPMI_CLEAR_MSG_FLAGS_CMD 0x30 +#define IPMI_GET_DEVICE_GUID_CMD 0x08 #define IPMI_GET_MSG_FLAGS_CMD 0x31 #define IPMI_SEND_MSG_CMD 0x34 #define IPMI_GET_MSG_CMD 0x33 diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index e36ee157ad67..53571288a9fc 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -37,6 +37,9 @@ #include #include #include +#include +#include +#include /* This files describes the interface for IPMI system management interface drivers to bind into the IPMI message handler. */ @@ -113,12 +116,52 @@ struct ipmi_smi_handlers void (*dec_usecount)(void *send_info); }; +struct ipmi_device_id { + unsigned char device_id; + unsigned char device_revision; + unsigned char firmware_revision_1; + unsigned char firmware_revision_2; + unsigned char ipmi_version; + unsigned char additional_device_support; + unsigned int manufacturer_id; + unsigned int product_id; + unsigned char aux_firmware_revision[4]; + unsigned int aux_firmware_revision_set : 1; +}; + +#define ipmi_version_major(v) ((v)->ipmi_version & 0xf) +#define ipmi_version_minor(v) ((v)->ipmi_version >> 4) + +/* Take a pointer to a raw data buffer and a length and extract device + id information from it. The first byte of data must point to the + byte from the get device id response after the completion code. + The caller is responsible for making sure the length is at least + 11 and the command completed without error. */ +static inline void ipmi_demangle_device_id(unsigned char *data, + unsigned int data_len, + struct ipmi_device_id *id) +{ + id->device_id = data[0]; + id->device_revision = data[1]; + id->firmware_revision_1 = data[2]; + id->firmware_revision_2 = data[3]; + id->ipmi_version = data[4]; + id->additional_device_support = data[5]; + id->manufacturer_id = data[6] | (data[7] << 8) | (data[8] << 16); + id->product_id = data[9] | (data[10] << 8); + if (data_len >= 15) { + memcpy(id->aux_firmware_revision, data+11, 4); + id->aux_firmware_revision_set = 1; + } else + id->aux_firmware_revision_set = 0; +} + /* Add a low-level interface to the IPMI driver. Note that if the interface doesn't know its slave address, it should pass in zero. */ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, - unsigned char version_major, - unsigned char version_minor, + struct ipmi_device_id *device_id, + struct device *dev, unsigned char slave_addr, ipmi_smi_t *intf); -- cgit v1.2.3 From 878a9f30d7b13015f3aa4534d7877d985f150183 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 26 Mar 2006 01:37:23 -0800 Subject: [PATCH] hpet header sanitization Add __KERNEL__ block. Use __KERNEL__ to allow ioctl interface to be usable. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hpet.h | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/hpet.h b/include/linux/hpet.h index 27238194b212..707f7cb9e795 100644 --- a/include/linux/hpet.h +++ b/include/linux/hpet.h @@ -3,6 +3,8 @@ #include +#ifdef __KERNEL__ + /* * Offsets into HPET Registers */ @@ -85,22 +87,6 @@ struct hpet { #define Tn_FSB_INT_ADDR_SHIFT (32UL) #define Tn_FSB_INT_VAL_MASK (0x00000000ffffffffULL) -struct hpet_info { - unsigned long hi_ireqfreq; /* Hz */ - unsigned long hi_flags; /* information */ - unsigned short hi_hpet; - unsigned short hi_timer; -}; - -#define HPET_INFO_PERIODIC 0x0001 /* timer is periodic */ - -#define HPET_IE_ON _IO('h', 0x01) /* interrupt on */ -#define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */ -#define HPET_INFO _IOR('h', 0x03, struct hpet_info) -#define HPET_EPI _IO('h', 0x04) /* enable periodic */ -#define HPET_DPI _IO('h', 0x05) /* disable periodic */ -#define HPET_IRQFREQ _IOW('h', 0x6, unsigned long) /* IRQFREQ usec */ - /* * exported interfaces */ @@ -133,4 +119,22 @@ int hpet_register(struct hpet_task *, int); int hpet_unregister(struct hpet_task *); int hpet_control(struct hpet_task *, unsigned int, unsigned long); +#endif /* __KERNEL__ */ + +struct hpet_info { + unsigned long hi_ireqfreq; /* Hz */ + unsigned long hi_flags; /* information */ + unsigned short hi_hpet; + unsigned short hi_timer; +}; + +#define HPET_INFO_PERIODIC 0x0001 /* timer is periodic */ + +#define HPET_IE_ON _IO('h', 0x01) /* interrupt on */ +#define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */ +#define HPET_INFO _IOR('h', 0x03, struct hpet_info) +#define HPET_EPI _IO('h', 0x04) /* enable periodic */ +#define HPET_DPI _IO('h', 0x05) /* disable periodic */ +#define HPET_IRQFREQ _IOW('h', 0x6, unsigned long) /* IRQFREQ usec */ + #endif /* !__HPET__ */ -- cgit v1.2.3 From 5842add2f3b519111b6401f3a35862bd00a3aa7e Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Sun, 26 Mar 2006 01:37:26 -0800 Subject: [PATCH] VFS,fs/locks.c,NFSD4: add race_free posix_lock_file_conf() interface Lockd and the NFSv4 server both exercise a race condition where posix_test_lock() is called either before or after posix_lock_file() to deal with a denied lock request due to a conflicting lock. Remove the race condition for the NFSv4 server by adding a new conflicting lock parameter to __posix_lock_file() , changing the name to __posix_lock_file_conf(). Keep posix_lock_file() interface, add posix_lock_conf() interface, both call __posix_lock_file_conf(). [akpm@osdl.org: Put the EXPORT_SYMBOL() where it belongs] Signed-off-by: Andy Adamson Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/locks.c | 32 ++++++++++++++++++++++++-------- include/linux/fs.h | 1 + 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/fs/locks.c b/fs/locks.c index 4badf6a0e7b6..4d9e71d43e7e 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -792,9 +792,7 @@ out: return error; } -EXPORT_SYMBOL(posix_lock_file); - -static int __posix_lock_file(struct inode *inode, struct file_lock *request) +static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request, struct file_lock *conflock) { struct file_lock *fl; struct file_lock *new_fl, *new_fl2; @@ -818,6 +816,8 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request) continue; if (!posix_locks_conflict(request, fl)) continue; + if (conflock) + locks_copy_lock(conflock, fl); error = -EAGAIN; if (!(request->fl_flags & FL_SLEEP)) goto out; @@ -987,8 +987,24 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request) */ int posix_lock_file(struct file *filp, struct file_lock *fl) { - return __posix_lock_file(filp->f_dentry->d_inode, fl); + return __posix_lock_file_conf(filp->f_dentry->d_inode, fl, NULL); +} +EXPORT_SYMBOL(posix_lock_file); + +/** + * posix_lock_file_conf - Apply a POSIX-style lock to a file + * @filp: The file to apply the lock to + * @fl: The lock to be applied + * @conflock: Place to return a copy of the conflicting lock, if found. + * + * Except for the conflock parameter, acts just like posix_lock_file. + */ +int posix_lock_file_conf(struct file *filp, struct file_lock *fl, + struct file_lock *conflock) +{ + return __posix_lock_file_conf(filp->f_dentry->d_inode, fl, conflock); } +EXPORT_SYMBOL(posix_lock_file_conf); /** * posix_lock_file_wait - Apply a POSIX-style lock to a file @@ -1004,7 +1020,7 @@ int posix_lock_file_wait(struct file *filp, struct file_lock *fl) int error; might_sleep (); for (;;) { - error = __posix_lock_file(filp->f_dentry->d_inode, fl); + error = posix_lock_file(filp, fl); if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); @@ -1076,7 +1092,7 @@ int locks_mandatory_area(int read_write, struct inode *inode, fl.fl_end = offset + count - 1; for (;;) { - error = __posix_lock_file(inode, &fl); + error = __posix_lock_file_conf(inode, &fl, NULL); if (error != -EAGAIN) break; if (!(fl.fl_flags & FL_SLEEP)) @@ -1689,7 +1705,7 @@ again: error = filp->f_op->lock(filp, cmd, file_lock); else { for (;;) { - error = __posix_lock_file(inode, file_lock); + error = posix_lock_file(filp, file_lock); if ((error != -EAGAIN) || (cmd == F_SETLK)) break; error = wait_event_interruptible(file_lock->fl_wait, @@ -1832,7 +1848,7 @@ again: error = filp->f_op->lock(filp, cmd, file_lock); else { for (;;) { - error = __posix_lock_file(inode, file_lock); + error = posix_lock_file(filp, file_lock); if ((error != -EAGAIN) || (cmd == F_SETLK64)) break; error = wait_event_interruptible(file_lock->fl_wait, diff --git a/include/linux/fs.h b/include/linux/fs.h index 9674679525f9..ab67181a5a55 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -763,6 +763,7 @@ extern void locks_copy_lock(struct file_lock *, struct file_lock *); extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_flock(struct file *); extern int posix_test_lock(struct file *, struct file_lock *, struct file_lock *); +extern int posix_lock_file_conf(struct file *, struct file_lock *, struct file_lock *); extern int posix_lock_file(struct file *, struct file_lock *); extern int posix_lock_file_wait(struct file *, struct file_lock *); extern int posix_unblock_lock(struct file *, struct file_lock *); -- cgit v1.2.3 From 88959ea968709c35e8b979ac9f5a398fa748091a Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Sun, 26 Mar 2006 01:37:27 -0800 Subject: [PATCH] create struct compat_timex and use it everywhere We had a copy of the compatibility version of struct timex in each 64 bit architecture. This patch just creates a global one and replaces all the usages of the old ones. Signed-off-by: Stephen Rothwell Cc: Arnd Bergmann Acked-by: Kyle McMartin Acked-by: Tony Luck Acked-by: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/ia32/sys_ia32.c | 15 +-------------- arch/mips/kernel/linux32.c | 15 +-------------- arch/parisc/kernel/sys_parisc32.c | 37 ++++--------------------------------- arch/powerpc/kernel/sys_ppc32.c | 15 +-------------- arch/s390/kernel/compat_linux.c | 15 +-------------- arch/sparc64/kernel/sys_sparc32.c | 15 +-------------- arch/x86_64/ia32/sys_ia32.c | 19 +++---------------- include/linux/compat.h | 26 ++++++++++++++++++++++++++ 8 files changed, 38 insertions(+), 119 deletions(-) (limited to 'include') diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 13e739e4c84d..1e1a2353aefd 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -2594,23 +2594,10 @@ sys32_setresgid(compat_gid_t rgid, compat_gid_t egid, /* Handle adjtimex compatibility. */ -struct timex32 { - u32 modes; - s32 offset, freq, maxerror, esterror; - s32 status, constant, precision, tolerance; - struct compat_timeval time; - s32 tick; - s32 ppsfreq, jitter, shift, stabil; - s32 jitcnt, calcnt, errcnt, stbcnt; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; -}; - extern int do_adjtimex(struct timex *); asmlinkage long -sys32_adjtimex(struct timex32 *utp) +sys32_adjtimex(struct compat_timex *utp) { struct timex txc; int ret; diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index 013bc93688e8..f33e779796f0 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -1159,22 +1159,9 @@ out: /* Handle adjtimex compatibility. */ -struct timex32 { - u32 modes; - s32 offset, freq, maxerror, esterror; - s32 status, constant, precision, tolerance; - struct compat_timeval time; - s32 tick; - s32 ppsfreq, jitter, shift, stabil; - s32 jitcnt, calcnt, errcnt, stbcnt; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; -}; - extern int do_adjtimex(struct timex *); -asmlinkage int sys32_adjtimex(struct timex32 __user *utp) +asmlinkage int sys32_adjtimex(struct compat_timex __user *utp) { struct timex txc; int ret; diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c index 613569018410..ca1d8dbbadf3 100644 --- a/arch/parisc/kernel/sys_parisc32.c +++ b/arch/parisc/kernel/sys_parisc32.c @@ -567,43 +567,14 @@ asmlinkage int sys32_sendfile64(int out_fd, int in_fd, compat_loff_t __user *off } -struct timex32 { - unsigned int modes; /* mode selector */ - int offset; /* time offset (usec) */ - int freq; /* frequency offset (scaled ppm) */ - int maxerror; /* maximum error (usec) */ - int esterror; /* estimated error (usec) */ - int status; /* clock command/status */ - int constant; /* pll time constant */ - int precision; /* clock precision (usec) (read only) */ - int tolerance; /* clock frequency tolerance (ppm) - * (read only) - */ - struct compat_timeval time; /* (read only) */ - int tick; /* (modified) usecs between clock ticks */ - - int ppsfreq; /* pps frequency (scaled ppm) (ro) */ - int jitter; /* pps jitter (us) (ro) */ - int shift; /* interval duration (s) (shift) (ro) */ - int stabil; /* pps stability (scaled ppm) (ro) */ - int jitcnt; /* jitter limit exceeded (ro) */ - int calcnt; /* calibration intervals (ro) */ - int errcnt; /* calibration errors (ro) */ - int stbcnt; /* stability limit exceeded (ro) */ - - int :32; int :32; int :32; int :32; - int :32; int :32; int :32; int :32; - int :32; int :32; int :32; int :32; -}; - -asmlinkage long sys32_adjtimex(struct timex32 __user *txc_p32) +asmlinkage long sys32_adjtimex(struct compat_timex __user *txc_p32) { struct timex txc; - struct timex32 t32; + struct compat_timex t32; int ret; extern int do_adjtimex(struct timex *txc); - if(copy_from_user(&t32, txc_p32, sizeof(struct timex32))) + if(copy_from_user(&t32, txc_p32, sizeof(struct compat_timex))) return -EFAULT; #undef CP #define CP(x) txc.x = t32.x @@ -620,7 +591,7 @@ asmlinkage long sys32_adjtimex(struct timex32 __user *txc_p32) CP(time.tv_sec); CP(time.tv_usec); CP(tick); CP(ppsfreq); CP(jitter); CP(shift); CP(stabil); CP(jitcnt); CP(calcnt); CP(errcnt); CP(stbcnt); - return copy_to_user(txc_p32, &t32, sizeof(struct timex32)) ? -EFAULT : ret; + return copy_to_user(txc_p32, &t32, sizeof(struct compat_timex)) ? -EFAULT : ret; } diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index cd75ab2908fa..d9867f583c68 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -162,22 +162,9 @@ asmlinkage long compat_sys_sysfs(u32 option, u32 arg1, u32 arg2) } /* Handle adjtimex compatibility. */ -struct timex32 { - u32 modes; - s32 offset, freq, maxerror, esterror; - s32 status, constant, precision, tolerance; - struct compat_timeval time; - s32 tick; - s32 ppsfreq, jitter, shift, stabil; - s32 jitcnt, calcnt, errcnt, stbcnt; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; -}; - extern int do_adjtimex(struct timex *); -asmlinkage long compat_sys_adjtimex(struct timex32 __user *utp) +asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp) { struct timex txc; int ret; diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c index cc058dc3bc8b..9809264f2f4d 100644 --- a/arch/s390/kernel/compat_linux.c +++ b/arch/s390/kernel/compat_linux.c @@ -707,22 +707,9 @@ asmlinkage long sys32_sendfile64(int out_fd, int in_fd, /* Handle adjtimex compatibility. */ -struct timex32 { - u32 modes; - s32 offset, freq, maxerror, esterror; - s32 status, constant, precision, tolerance; - struct compat_timeval time; - s32 tick; - s32 ppsfreq, jitter, shift, stabil; - s32 jitcnt, calcnt, errcnt, stbcnt; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; -}; - extern int do_adjtimex(struct timex *); -asmlinkage long sys32_adjtimex(struct timex32 __user *utp) +asmlinkage long sys32_adjtimex(struct compat_timex __user *utp) { struct timex txc; int ret; diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 0e41df024489..48f02f201918 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -947,22 +947,9 @@ asmlinkage long compat_sys_sendfile64(int out_fd, int in_fd, /* Handle adjtimex compatibility. */ -struct timex32 { - u32 modes; - s32 offset, freq, maxerror, esterror; - s32 status, constant, precision, tolerance; - struct compat_timeval time; - s32 tick; - s32 ppsfreq, jitter, shift, stabil; - s32 jitcnt, calcnt, errcnt, stbcnt; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; -}; - extern int do_adjtimex(struct timex *); -asmlinkage long sys32_adjtimex(struct timex32 __user *utp) +asmlinkage long sys32_adjtimex(struct compat_timex __user *utp) { struct timex txc; int ret; diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c index 2b2d029f477c..b13121e451a8 100644 --- a/arch/x86_64/ia32/sys_ia32.c +++ b/arch/x86_64/ia32/sys_ia32.c @@ -769,30 +769,17 @@ sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, s32 count) /* Handle adjtimex compatibility. */ -struct timex32 { - u32 modes; - s32 offset, freq, maxerror, esterror; - s32 status, constant, precision, tolerance; - struct compat_timeval time; - s32 tick; - s32 ppsfreq, jitter, shift, stabil; - s32 jitcnt, calcnt, errcnt, stbcnt; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; - s32 :32; s32 :32; s32 :32; s32 :32; -}; - extern int do_adjtimex(struct timex *); asmlinkage long -sys32_adjtimex(struct timex32 __user *utp) +sys32_adjtimex(struct compat_timex __user *utp) { struct timex txc; int ret; memset(&txc, 0, sizeof(struct timex)); - if (!access_ok(VERIFY_READ, utp, sizeof(struct timex32)) || + if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) || __get_user(txc.modes, &utp->modes) || __get_user(txc.offset, &utp->offset) || __get_user(txc.freq, &utp->freq) || @@ -817,7 +804,7 @@ sys32_adjtimex(struct timex32 __user *utp) ret = do_adjtimex(&txc); - if (!access_ok(VERIFY_WRITE, utp, sizeof(struct timex32)) || + if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) || __put_user(txc.modes, &utp->modes) || __put_user(txc.offset, &utp->offset) || __put_user(txc.freq, &utp->freq) || diff --git a/include/linux/compat.h b/include/linux/compat.h index c9ab2a26348c..859f95700d36 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -45,6 +45,32 @@ struct compat_tms { compat_clock_t tms_cstime; }; +struct compat_timex { + compat_uint_t modes; + compat_long_t offset; + compat_long_t freq; + compat_long_t maxerror; + compat_long_t esterror; + compat_int_t status; + compat_long_t constant; + compat_long_t precision; + compat_long_t tolerance; + struct compat_timeval time; + compat_long_t tick; + compat_long_t ppsfreq; + compat_long_t jitter; + compat_int_t shift; + compat_long_t stabil; + compat_long_t jitcnt; + compat_long_t calcnt; + compat_long_t errcnt; + compat_long_t stbcnt; + + compat_int_t :32; compat_int_t :32; compat_int_t :32; compat_int_t :32; + compat_int_t :32; compat_int_t :32; compat_int_t :32; compat_int_t :32; + compat_int_t :32; compat_int_t :32; compat_int_t :32; compat_int_t :32; +}; + #define _COMPAT_NSIG_WORDS (_COMPAT_NSIG / _COMPAT_NSIG_BPW) typedef struct { -- cgit v1.2.3 From 3158e9411a66fb98d495ac441c242264f31aaf3e Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Sun, 26 Mar 2006 01:37:29 -0800 Subject: [PATCH] consolidate sys32/compat_adjtimex Create compat_sys_adjtimex and use it an all appropriate places. Signed-off-by: Stephen Rothwell Cc: Arnd Bergmann Acked-by: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/kernel/osf_sys.c | 1 - arch/ia64/ia32/sys_ia32.c | 62 ------------------------------------ arch/mips/kernel/linux32.c | 61 ------------------------------------ arch/mips/kernel/scall64-n32.S | 2 +- arch/mips/kernel/scall64-o32.S | 2 +- arch/parisc/kernel/sys_parisc32.c | 29 ----------------- arch/parisc/kernel/syscall_table.S | 2 +- arch/powerpc/kernel/sys_ppc32.c | 60 ----------------------------------- arch/s390/kernel/compat_linux.c | 61 ------------------------------------ arch/s390/kernel/compat_wrapper.S | 8 ++--- arch/s390/kernel/syscalls.S | 2 +- arch/sparc64/kernel/sys_sparc32.c | 61 ------------------------------------ arch/sparc64/kernel/systbls.S | 2 +- arch/x86_64/ia32/ia32entry.S | 2 +- arch/x86_64/ia32/sys_ia32.c | 64 -------------------------------------- include/linux/compat.h | 2 ++ include/linux/timex.h | 2 ++ kernel/compat.c | 59 +++++++++++++++++++++++++++++++++++ 18 files changed, 73 insertions(+), 409 deletions(-) (limited to 'include') diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 7fb14f42a125..31afe3d91ac6 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -821,7 +821,6 @@ osf_setsysinfo(unsigned long op, void __user *buffer, unsigned long nbytes, affects all sorts of things, like timeval and itimerval. */ extern struct timezone sys_tz; -extern int do_adjtimex(struct timex *); struct timeval32 { diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 1e1a2353aefd..5366b3b23d09 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -2591,65 +2590,4 @@ sys32_setresgid(compat_gid_t rgid, compat_gid_t egid, ssgid = (sgid == (compat_gid_t)-1) ? ((gid_t)-1) : ((gid_t)sgid); return sys_setresgid(srgid, segid, ssgid); } - -/* Handle adjtimex compatibility. */ - -extern int do_adjtimex(struct timex *); - -asmlinkage long -sys32_adjtimex(struct compat_timex *utp) -{ - struct timex txc; - int ret; - - memset(&txc, 0, sizeof(struct timex)); - - if(get_user(txc.modes, &utp->modes) || - __get_user(txc.offset, &utp->offset) || - __get_user(txc.freq, &utp->freq) || - __get_user(txc.maxerror, &utp->maxerror) || - __get_user(txc.esterror, &utp->esterror) || - __get_user(txc.status, &utp->status) || - __get_user(txc.constant, &utp->constant) || - __get_user(txc.precision, &utp->precision) || - __get_user(txc.tolerance, &utp->tolerance) || - __get_user(txc.time.tv_sec, &utp->time.tv_sec) || - __get_user(txc.time.tv_usec, &utp->time.tv_usec) || - __get_user(txc.tick, &utp->tick) || - __get_user(txc.ppsfreq, &utp->ppsfreq) || - __get_user(txc.jitter, &utp->jitter) || - __get_user(txc.shift, &utp->shift) || - __get_user(txc.stabil, &utp->stabil) || - __get_user(txc.jitcnt, &utp->jitcnt) || - __get_user(txc.calcnt, &utp->calcnt) || - __get_user(txc.errcnt, &utp->errcnt) || - __get_user(txc.stbcnt, &utp->stbcnt)) - return -EFAULT; - - ret = do_adjtimex(&txc); - - if(put_user(txc.modes, &utp->modes) || - __put_user(txc.offset, &utp->offset) || - __put_user(txc.freq, &utp->freq) || - __put_user(txc.maxerror, &utp->maxerror) || - __put_user(txc.esterror, &utp->esterror) || - __put_user(txc.status, &utp->status) || - __put_user(txc.constant, &utp->constant) || - __put_user(txc.precision, &utp->precision) || - __put_user(txc.tolerance, &utp->tolerance) || - __put_user(txc.time.tv_sec, &utp->time.tv_sec) || - __put_user(txc.time.tv_usec, &utp->time.tv_usec) || - __put_user(txc.tick, &utp->tick) || - __put_user(txc.ppsfreq, &utp->ppsfreq) || - __put_user(txc.jitter, &utp->jitter) || - __put_user(txc.shift, &utp->shift) || - __put_user(txc.stabil, &utp->stabil) || - __put_user(txc.jitcnt, &utp->jitcnt) || - __put_user(txc.calcnt, &utp->calcnt) || - __put_user(txc.errcnt, &utp->errcnt) || - __put_user(txc.stbcnt, &utp->stbcnt)) - ret = -EFAULT; - - return ret; -} #endif /* NOTYET */ diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index f33e779796f0..3f40c37a9ee6 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -1157,66 +1156,6 @@ out: return err; } -/* Handle adjtimex compatibility. */ - -extern int do_adjtimex(struct timex *); - -asmlinkage int sys32_adjtimex(struct compat_timex __user *utp) -{ - struct timex txc; - int ret; - - memset(&txc, 0, sizeof(struct timex)); - - if (get_user(txc.modes, &utp->modes) || - __get_user(txc.offset, &utp->offset) || - __get_user(txc.freq, &utp->freq) || - __get_user(txc.maxerror, &utp->maxerror) || - __get_user(txc.esterror, &utp->esterror) || - __get_user(txc.status, &utp->status) || - __get_user(txc.constant, &utp->constant) || - __get_user(txc.precision, &utp->precision) || - __get_user(txc.tolerance, &utp->tolerance) || - __get_user(txc.time.tv_sec, &utp->time.tv_sec) || - __get_user(txc.time.tv_usec, &utp->time.tv_usec) || - __get_user(txc.tick, &utp->tick) || - __get_user(txc.ppsfreq, &utp->ppsfreq) || - __get_user(txc.jitter, &utp->jitter) || - __get_user(txc.shift, &utp->shift) || - __get_user(txc.stabil, &utp->stabil) || - __get_user(txc.jitcnt, &utp->jitcnt) || - __get_user(txc.calcnt, &utp->calcnt) || - __get_user(txc.errcnt, &utp->errcnt) || - __get_user(txc.stbcnt, &utp->stbcnt)) - return -EFAULT; - - ret = do_adjtimex(&txc); - - if (put_user(txc.modes, &utp->modes) || - __put_user(txc.offset, &utp->offset) || - __put_user(txc.freq, &utp->freq) || - __put_user(txc.maxerror, &utp->maxerror) || - __put_user(txc.esterror, &utp->esterror) || - __put_user(txc.status, &utp->status) || - __put_user(txc.constant, &utp->constant) || - __put_user(txc.precision, &utp->precision) || - __put_user(txc.tolerance, &utp->tolerance) || - __put_user(txc.time.tv_sec, &utp->time.tv_sec) || - __put_user(txc.time.tv_usec, &utp->time.tv_usec) || - __put_user(txc.tick, &utp->tick) || - __put_user(txc.ppsfreq, &utp->ppsfreq) || - __put_user(txc.jitter, &utp->jitter) || - __put_user(txc.shift, &utp->shift) || - __put_user(txc.stabil, &utp->stabil) || - __put_user(txc.jitcnt, &utp->jitcnt) || - __put_user(txc.calcnt, &utp->calcnt) || - __put_user(txc.errcnt, &utp->errcnt) || - __put_user(txc.stbcnt, &utp->stbcnt)) - ret = -EFAULT; - - return ret; -} - asmlinkage int sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, s32 count) { diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 02c8267e45e7..05a2c0567dae 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -273,7 +273,7 @@ EXPORT(sysn32_call_table) PTR sys_pivot_root PTR sys32_sysctl PTR sys_prctl - PTR sys32_adjtimex + PTR compat_sys_adjtimex PTR compat_sys_setrlimit /* 6155 */ PTR sys_chroot PTR sys_sync diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 797e0d874889..19c4ca481b02 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -328,7 +328,7 @@ sys_call_table: PTR sys_setdomainname PTR sys32_newuname PTR sys_ni_syscall /* sys_modify_ldt */ - PTR sys32_adjtimex + PTR compat_sys_adjtimex PTR sys_mprotect /* 4125 */ PTR compat_sys_sigprocmask PTR sys_ni_syscall /* was creat_module */ diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c index ca1d8dbbadf3..d286f68a3d3a 100644 --- a/arch/parisc/kernel/sys_parisc32.c +++ b/arch/parisc/kernel/sys_parisc32.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -567,34 +566,6 @@ asmlinkage int sys32_sendfile64(int out_fd, int in_fd, compat_loff_t __user *off } -asmlinkage long sys32_adjtimex(struct compat_timex __user *txc_p32) -{ - struct timex txc; - struct compat_timex t32; - int ret; - extern int do_adjtimex(struct timex *txc); - - if(copy_from_user(&t32, txc_p32, sizeof(struct compat_timex))) - return -EFAULT; -#undef CP -#define CP(x) txc.x = t32.x - CP(modes); CP(offset); CP(freq); CP(maxerror); CP(esterror); - CP(status); CP(constant); CP(precision); CP(tolerance); - CP(time.tv_sec); CP(time.tv_usec); CP(tick); CP(ppsfreq); CP(jitter); - CP(shift); CP(stabil); CP(jitcnt); CP(calcnt); CP(errcnt); - CP(stbcnt); - ret = do_adjtimex(&txc); -#undef CP -#define CP(x) t32.x = txc.x - CP(modes); CP(offset); CP(freq); CP(maxerror); CP(esterror); - CP(status); CP(constant); CP(precision); CP(tolerance); - CP(time.tv_sec); CP(time.tv_usec); CP(tick); CP(ppsfreq); CP(jitter); - CP(shift); CP(stabil); CP(jitcnt); CP(calcnt); CP(errcnt); - CP(stbcnt); - return copy_to_user(txc_p32, &t32, sizeof(struct compat_timex)) ? -EFAULT : ret; -} - - struct sysinfo32 { s32 uptime; u32 loads[3]; diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index 71011eadb872..89b6c56ea0a8 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -207,7 +207,7 @@ /* struct sockaddr... */ ENTRY_SAME(recvfrom) /* struct timex contains longs */ - ENTRY_DIFF(adjtimex) + ENTRY_COMP(adjtimex) ENTRY_SAME(mprotect) /* 125 */ /* old_sigset_t forced to 32 bits. Beware glibc sigset_t */ ENTRY_COMP(sigprocmask) diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index d9867f583c68..ec274e688816 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -161,65 +160,6 @@ asmlinkage long compat_sys_sysfs(u32 option, u32 arg1, u32 arg2) return sys_sysfs((int)option, arg1, arg2); } -/* Handle adjtimex compatibility. */ -extern int do_adjtimex(struct timex *); - -asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp) -{ - struct timex txc; - int ret; - - memset(&txc, 0, sizeof(struct timex)); - - if(get_user(txc.modes, &utp->modes) || - __get_user(txc.offset, &utp->offset) || - __get_user(txc.freq, &utp->freq) || - __get_user(txc.maxerror, &utp->maxerror) || - __get_user(txc.esterror, &utp->esterror) || - __get_user(txc.status, &utp->status) || - __get_user(txc.constant, &utp->constant) || - __get_user(txc.precision, &utp->precision) || - __get_user(txc.tolerance, &utp->tolerance) || - __get_user(txc.time.tv_sec, &utp->time.tv_sec) || - __get_user(txc.time.tv_usec, &utp->time.tv_usec) || - __get_user(txc.tick, &utp->tick) || - __get_user(txc.ppsfreq, &utp->ppsfreq) || - __get_user(txc.jitter, &utp->jitter) || - __get_user(txc.shift, &utp->shift) || - __get_user(txc.stabil, &utp->stabil) || - __get_user(txc.jitcnt, &utp->jitcnt) || - __get_user(txc.calcnt, &utp->calcnt) || - __get_user(txc.errcnt, &utp->errcnt) || - __get_user(txc.stbcnt, &utp->stbcnt)) - return -EFAULT; - - ret = do_adjtimex(&txc); - - if(put_user(txc.modes, &utp->modes) || - __put_user(txc.offset, &utp->offset) || - __put_user(txc.freq, &utp->freq) || - __put_user(txc.maxerror, &utp->maxerror) || - __put_user(txc.esterror, &utp->esterror) || - __put_user(txc.status, &utp->status) || - __put_user(txc.constant, &utp->constant) || - __put_user(txc.precision, &utp->precision) || - __put_user(txc.tolerance, &utp->tolerance) || - __put_user(txc.time.tv_sec, &utp->time.tv_sec) || - __put_user(txc.time.tv_usec, &utp->time.tv_usec) || - __put_user(txc.tick, &utp->tick) || - __put_user(txc.ppsfreq, &utp->ppsfreq) || - __put_user(txc.jitter, &utp->jitter) || - __put_user(txc.shift, &utp->shift) || - __put_user(txc.stabil, &utp->stabil) || - __put_user(txc.jitcnt, &utp->jitcnt) || - __put_user(txc.calcnt, &utp->calcnt) || - __put_user(txc.errcnt, &utp->errcnt) || - __put_user(txc.stbcnt, &utp->stbcnt)) - ret = -EFAULT; - - return ret; -} - asmlinkage long compat_sys_pause(void) { current->state = TASK_INTERRUPTIBLE; diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c index 9809264f2f4d..5e14de37c17b 100644 --- a/arch/s390/kernel/compat_linux.c +++ b/arch/s390/kernel/compat_linux.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -705,66 +704,6 @@ asmlinkage long sys32_sendfile64(int out_fd, int in_fd, return ret; } -/* Handle adjtimex compatibility. */ - -extern int do_adjtimex(struct timex *); - -asmlinkage long sys32_adjtimex(struct compat_timex __user *utp) -{ - struct timex txc; - int ret; - - memset(&txc, 0, sizeof(struct timex)); - - if(get_user(txc.modes, &utp->modes) || - __get_user(txc.offset, &utp->offset) || - __get_user(txc.freq, &utp->freq) || - __get_user(txc.maxerror, &utp->maxerror) || - __get_user(txc.esterror, &utp->esterror) || - __get_user(txc.status, &utp->status) || - __get_user(txc.constant, &utp->constant) || - __get_user(txc.precision, &utp->precision) || - __get_user(txc.tolerance, &utp->tolerance) || - __get_user(txc.time.tv_sec, &utp->time.tv_sec) || - __get_user(txc.time.tv_usec, &utp->time.tv_usec) || - __get_user(txc.tick, &utp->tick) || - __get_user(txc.ppsfreq, &utp->ppsfreq) || - __get_user(txc.jitter, &utp->jitter) || - __get_user(txc.shift, &utp->shift) || - __get_user(txc.stabil, &utp->stabil) || - __get_user(txc.jitcnt, &utp->jitcnt) || - __get_user(txc.calcnt, &utp->calcnt) || - __get_user(txc.errcnt, &utp->errcnt) || - __get_user(txc.stbcnt, &utp->stbcnt)) - return -EFAULT; - - ret = do_adjtimex(&txc); - - if(put_user(txc.modes, &utp->modes) || - __put_user(txc.offset, &utp->offset) || - __put_user(txc.freq, &utp->freq) || - __put_user(txc.maxerror, &utp->maxerror) || - __put_user(txc.esterror, &utp->esterror) || - __put_user(txc.status, &utp->status) || - __put_user(txc.constant, &utp->constant) || - __put_user(txc.precision, &utp->precision) || - __put_user(txc.tolerance, &utp->tolerance) || - __put_user(txc.time.tv_sec, &utp->time.tv_sec) || - __put_user(txc.time.tv_usec, &utp->time.tv_usec) || - __put_user(txc.tick, &utp->tick) || - __put_user(txc.ppsfreq, &utp->ppsfreq) || - __put_user(txc.jitter, &utp->jitter) || - __put_user(txc.shift, &utp->shift) || - __put_user(txc.stabil, &utp->stabil) || - __put_user(txc.jitcnt, &utp->jitcnt) || - __put_user(txc.calcnt, &utp->calcnt) || - __put_user(txc.errcnt, &utp->errcnt) || - __put_user(txc.stbcnt, &utp->stbcnt)) - ret = -EFAULT; - - return ret; -} - #ifdef CONFIG_SYSCTL struct __sysctl_args32 { u32 name; diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 50e80138e7ad..199da68bd7be 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -551,10 +551,10 @@ sys32_newuname_wrapper: llgtr %r2,%r2 # struct new_utsname * jg s390x_newuname # branch to system call - .globl sys32_adjtimex_wrapper -sys32_adjtimex_wrapper: - llgtr %r2,%r2 # struct timex_emu31 * - jg sys32_adjtimex # branch to system call + .globl compat_sys_adjtimex_wrapper +compat_sys_adjtimex_wrapper: + llgtr %r2,%r2 # struct compat_timex * + jg compat_sys_adjtimex # branch to system call .globl sys32_mprotect_wrapper sys32_mprotect_wrapper: diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index 7c88d85c3597..2f56654da821 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -132,7 +132,7 @@ SYSCALL(sys_clone_glue,sys_clone_glue,sys32_clone_glue) /* 120 */ SYSCALL(sys_setdomainname,sys_setdomainname,sys32_setdomainname_wrapper) SYSCALL(sys_newuname,s390x_newuname,sys32_newuname_wrapper) NI_SYSCALL /* modify_ldt for i386 */ -SYSCALL(sys_adjtimex,sys_adjtimex,sys32_adjtimex_wrapper) +SYSCALL(sys_adjtimex,sys_adjtimex,compat_sys_adjtimex_wrapper) SYSCALL(sys_mprotect,sys_mprotect,sys32_mprotect_wrapper) /* 125 */ SYSCALL(sys_sigprocmask,sys_sigprocmask,compat_sys_sigprocmask_wrapper) NI_SYSCALL /* old "create module" */ diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 48f02f201918..2e906bad56fa 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -945,66 +944,6 @@ asmlinkage long compat_sys_sendfile64(int out_fd, int in_fd, return ret; } -/* Handle adjtimex compatibility. */ - -extern int do_adjtimex(struct timex *); - -asmlinkage long sys32_adjtimex(struct compat_timex __user *utp) -{ - struct timex txc; - int ret; - - memset(&txc, 0, sizeof(struct timex)); - - if (get_user(txc.modes, &utp->modes) || - __get_user(txc.offset, &utp->offset) || - __get_user(txc.freq, &utp->freq) || - __get_user(txc.maxerror, &utp->maxerror) || - __get_user(txc.esterror, &utp->esterror) || - __get_user(txc.status, &utp->status) || - __get_user(txc.constant, &utp->constant) || - __get_user(txc.precision, &utp->precision) || - __get_user(txc.tolerance, &utp->tolerance) || - __get_user(txc.time.tv_sec, &utp->time.tv_sec) || - __get_user(txc.time.tv_usec, &utp->time.tv_usec) || - __get_user(txc.tick, &utp->tick) || - __get_user(txc.ppsfreq, &utp->ppsfreq) || - __get_user(txc.jitter, &utp->jitter) || - __get_user(txc.shift, &utp->shift) || - __get_user(txc.stabil, &utp->stabil) || - __get_user(txc.jitcnt, &utp->jitcnt) || - __get_user(txc.calcnt, &utp->calcnt) || - __get_user(txc.errcnt, &utp->errcnt) || - __get_user(txc.stbcnt, &utp->stbcnt)) - return -EFAULT; - - ret = do_adjtimex(&txc); - - if (put_user(txc.modes, &utp->modes) || - __put_user(txc.offset, &utp->offset) || - __put_user(txc.freq, &utp->freq) || - __put_user(txc.maxerror, &utp->maxerror) || - __put_user(txc.esterror, &utp->esterror) || - __put_user(txc.status, &utp->status) || - __put_user(txc.constant, &utp->constant) || - __put_user(txc.precision, &utp->precision) || - __put_user(txc.tolerance, &utp->tolerance) || - __put_user(txc.time.tv_sec, &utp->time.tv_sec) || - __put_user(txc.time.tv_usec, &utp->time.tv_usec) || - __put_user(txc.tick, &utp->tick) || - __put_user(txc.ppsfreq, &utp->ppsfreq) || - __put_user(txc.jitter, &utp->jitter) || - __put_user(txc.shift, &utp->shift) || - __put_user(txc.stabil, &utp->stabil) || - __put_user(txc.jitcnt, &utp->jitcnt) || - __put_user(txc.calcnt, &utp->calcnt) || - __put_user(txc.errcnt, &utp->errcnt) || - __put_user(txc.stbcnt, &utp->stbcnt)) - ret = -EFAULT; - - return ret; -} - /* This is just a version for 32-bit applications which does * not force O_LARGEFILE on. */ diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index c3adb7ac167d..3b250f2318fd 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -63,7 +63,7 @@ sys_call_table32: /*200*/ .word sys32_ssetmask, sys_sigsuspend, compat_sys_newlstat, sys_uselib, compat_sys_old_readdir .word sys32_readahead, sys32_socketcall, sys32_syslog, sys32_lookup_dcookie, sys32_fadvise64 /*210*/ .word sys32_fadvise64_64, sys32_tgkill, sys32_waitpid, sys_swapoff, sys32_sysinfo - .word sys32_ipc, sys32_sigreturn, sys_clone, sys32_ioprio_get, sys32_adjtimex + .word sys32_ipc, sys32_sigreturn, sys_clone, sys32_ioprio_get, compat_sys_adjtimex /*220*/ .word sys32_sigprocmask, sys_ni_syscall, sys32_delete_module, sys_ni_syscall, sys32_getpgid .word sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys32_setfsuid16, sys32_setfsgid16 /*230*/ .word sys32_select, compat_sys_time, sys_nis_syscall, compat_sys_stime, compat_sys_statfs64 diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S index 00dee176c08e..7549a4389fbf 100644 --- a/arch/x86_64/ia32/ia32entry.S +++ b/arch/x86_64/ia32/ia32entry.S @@ -501,7 +501,7 @@ ia32_sys_call_table: .quad sys_setdomainname .quad sys_uname .quad sys_modify_ldt - .quad sys32_adjtimex + .quad compat_sys_adjtimex .quad sys32_mprotect /* 125 */ .quad compat_sys_sigprocmask .quad quiet_ni_syscall /* create_module */ diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c index b13121e451a8..f182b20858e2 100644 --- a/arch/x86_64/ia32/sys_ia32.c +++ b/arch/x86_64/ia32/sys_ia32.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -767,69 +766,6 @@ sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, s32 count) return ret; } -/* Handle adjtimex compatibility. */ - -extern int do_adjtimex(struct timex *); - -asmlinkage long -sys32_adjtimex(struct compat_timex __user *utp) -{ - struct timex txc; - int ret; - - memset(&txc, 0, sizeof(struct timex)); - - if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) || - __get_user(txc.modes, &utp->modes) || - __get_user(txc.offset, &utp->offset) || - __get_user(txc.freq, &utp->freq) || - __get_user(txc.maxerror, &utp->maxerror) || - __get_user(txc.esterror, &utp->esterror) || - __get_user(txc.status, &utp->status) || - __get_user(txc.constant, &utp->constant) || - __get_user(txc.precision, &utp->precision) || - __get_user(txc.tolerance, &utp->tolerance) || - __get_user(txc.time.tv_sec, &utp->time.tv_sec) || - __get_user(txc.time.tv_usec, &utp->time.tv_usec) || - __get_user(txc.tick, &utp->tick) || - __get_user(txc.ppsfreq, &utp->ppsfreq) || - __get_user(txc.jitter, &utp->jitter) || - __get_user(txc.shift, &utp->shift) || - __get_user(txc.stabil, &utp->stabil) || - __get_user(txc.jitcnt, &utp->jitcnt) || - __get_user(txc.calcnt, &utp->calcnt) || - __get_user(txc.errcnt, &utp->errcnt) || - __get_user(txc.stbcnt, &utp->stbcnt)) - return -EFAULT; - - ret = do_adjtimex(&txc); - - if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) || - __put_user(txc.modes, &utp->modes) || - __put_user(txc.offset, &utp->offset) || - __put_user(txc.freq, &utp->freq) || - __put_user(txc.maxerror, &utp->maxerror) || - __put_user(txc.esterror, &utp->esterror) || - __put_user(txc.status, &utp->status) || - __put_user(txc.constant, &utp->constant) || - __put_user(txc.precision, &utp->precision) || - __put_user(txc.tolerance, &utp->tolerance) || - __put_user(txc.time.tv_sec, &utp->time.tv_sec) || - __put_user(txc.time.tv_usec, &utp->time.tv_usec) || - __put_user(txc.tick, &utp->tick) || - __put_user(txc.ppsfreq, &utp->ppsfreq) || - __put_user(txc.jitter, &utp->jitter) || - __put_user(txc.shift, &utp->shift) || - __put_user(txc.stabil, &utp->stabil) || - __put_user(txc.jitcnt, &utp->jitcnt) || - __put_user(txc.calcnt, &utp->calcnt) || - __put_user(txc.errcnt, &utp->errcnt) || - __put_user(txc.stbcnt, &utp->stbcnt)) - ret = -EFAULT; - - return ret; -} - asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff) diff --git a/include/linux/compat.h b/include/linux/compat.h index 859f95700d36..24d659cdbafe 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -207,5 +207,7 @@ static inline int compat_timespec_compare(struct compat_timespec *lhs, return lhs->tv_nsec - rhs->tv_nsec; } +asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp); + #endif /* CONFIG_COMPAT */ #endif /* _LINUX_COMPAT_H */ diff --git a/include/linux/timex.h b/include/linux/timex.h index 82dc9ae79d37..03914b7e41b1 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -307,6 +307,8 @@ time_interpolator_reset(void) /* Returns how long ticks are at present, in ns / 2^(SHIFT_SCALE-10). */ extern u64 current_tick_length(void); +extern int do_adjtimex(struct timex *); + #endif /* KERNEL */ #endif /* LINUX_TIMEX_H */ diff --git a/kernel/compat.c b/kernel/compat.c index 8c9cd88b6785..b9bdd1271f44 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -898,3 +899,61 @@ asmlinkage long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat return -ERESTARTNOHAND; } #endif /* __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND */ + +asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp) +{ + struct timex txc; + int ret; + + memset(&txc, 0, sizeof(struct timex)); + + if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) || + __get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) || + __put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} -- cgit v1.2.3 From 22e6c1b39c648850438decd491f62d311800c7db Mon Sep 17 00:00:00 2001 From: Maneesh Soni Date: Sun, 26 Mar 2006 01:37:29 -0800 Subject: [PATCH] Use loff_t for size in struct proc_dir_entry Change proc_dir_entry->size to be loff_t to represent files like /proc/vmcore for 32bit systems with more than 4G memory. Needed for seeing correct size for /proc/vmcore for 32-bit systems with > 4G RAM. Signed-off-by: Maneesh Soni Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/proc_fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 6b12b0f661b4..cb224cf653b1 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -56,7 +56,7 @@ struct proc_dir_entry { nlink_t nlink; uid_t uid; gid_t gid; - unsigned long size; + loff_t size; struct inode_operations * proc_iops; struct file_operations * proc_fops; get_info_t *get_info; -- cgit v1.2.3 From 6e0678f394c7bd21bfa5d252b071a09e10e7a749 Mon Sep 17 00:00:00 2001 From: Matthew Dobson Date: Sun, 26 Mar 2006 01:37:44 -0800 Subject: [PATCH] mempool: add page allocator This will be used by the next patch in the series to replace duplicate mempool-backed page allocators in 2 places in the kernel. It is also likely that there will be more users in the future. Signed-off-by: Matthew Dobson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mempool.h | 12 ++++++++++++ mm/mempool.c | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'include') diff --git a/include/linux/mempool.h b/include/linux/mempool.h index f2427d7394b0..9787572e0ae7 100644 --- a/include/linux/mempool.h +++ b/include/linux/mempool.h @@ -38,4 +38,16 @@ extern void mempool_free(void *element, mempool_t *pool); void *mempool_alloc_slab(gfp_t gfp_mask, void *pool_data); void mempool_free_slab(void *element, void *pool_data); +/* + * A mempool_alloc_t and mempool_free_t for a simple page allocator that + * allocates pages of the order specified by pool_data + */ +void *mempool_alloc_pages(gfp_t gfp_mask, void *pool_data); +void mempool_free_pages(void *element, void *pool_data); +static inline mempool_t *mempool_create_page_pool(int min_nr, int order) +{ + return mempool_create(min_nr, mempool_alloc_pages, mempool_free_pages, + (void *)(long)order); +} + #endif /* _LINUX_MEMPOOL_H */ diff --git a/mm/mempool.c b/mm/mempool.c index f71893ed3543..45c0112ca7b2 100644 --- a/mm/mempool.c +++ b/mm/mempool.c @@ -289,3 +289,21 @@ void mempool_free_slab(void *element, void *pool_data) kmem_cache_free(mem, element); } EXPORT_SYMBOL(mempool_free_slab); + +/* + * A simple mempool-backed page allocator that allocates pages + * of the order specified by pool_data. + */ +void *mempool_alloc_pages(gfp_t gfp_mask, void *pool_data) +{ + int order = (int)(long)pool_data; + return alloc_pages(gfp_mask, order); +} +EXPORT_SYMBOL(mempool_alloc_pages); + +void mempool_free_pages(void *element, void *pool_data) +{ + int order = (int)(long)pool_data; + __free_pages(element, order); +} +EXPORT_SYMBOL(mempool_free_pages); -- cgit v1.2.3 From 53184082b070dfb077218828fdf839826102ed96 Mon Sep 17 00:00:00 2001 From: Matthew Dobson Date: Sun, 26 Mar 2006 01:37:46 -0800 Subject: [PATCH] mempool: add kmalloc allocator Add another allocator to the common mempool code: a kmalloc/kfree allocator This will be used by the next patch in the series to replace duplicate mempool-backed kmalloc allocators in several places in the kernel. It is also very likely that there will be more users in the future. Signed-off-by: Matthew Dobson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mempool.h | 12 ++++++++++++ mm/mempool.c | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/include/linux/mempool.h b/include/linux/mempool.h index 9787572e0ae7..d23dbf076b57 100644 --- a/include/linux/mempool.h +++ b/include/linux/mempool.h @@ -38,6 +38,18 @@ extern void mempool_free(void *element, mempool_t *pool); void *mempool_alloc_slab(gfp_t gfp_mask, void *pool_data); void mempool_free_slab(void *element, void *pool_data); +/* + * A mempool_alloc_t and mempool_free_t to kmalloc the amount of memory + * specified by pool_data + */ +void *mempool_kmalloc(gfp_t gfp_mask, void *pool_data); +void mempool_kfree(void *element, void *pool_data); +static inline mempool_t *mempool_create_kmalloc_pool(int min_nr, size_t size) +{ + return mempool_create(min_nr, mempool_kmalloc, mempool_kfree, + (void *) size); +} + /* * A mempool_alloc_t and mempool_free_t for a simple page allocator that * allocates pages of the order specified by pool_data diff --git a/mm/mempool.c b/mm/mempool.c index 45c0112ca7b2..a1397c6a4864 100644 --- a/mm/mempool.c +++ b/mm/mempool.c @@ -290,6 +290,23 @@ void mempool_free_slab(void *element, void *pool_data) } EXPORT_SYMBOL(mempool_free_slab); +/* + * A commonly used alloc and free fn that kmalloc/kfrees the amount of memory + * specfied by pool_data + */ +void *mempool_kmalloc(gfp_t gfp_mask, void *pool_data) +{ + size_t size = (size_t) pool_data; + return kmalloc(size, gfp_mask); +} +EXPORT_SYMBOL(mempool_kmalloc); + +void mempool_kfree(void *element, void *pool_data) +{ + kfree(element); +} +EXPORT_SYMBOL(mempool_kfree); + /* * A simple mempool-backed page allocator that allocates pages * of the order specified by pool_data. -- cgit v1.2.3 From f183323d3822dee4d7b3147a59b6e8987fe201e0 Mon Sep 17 00:00:00 2001 From: Matthew Dobson Date: Sun, 26 Mar 2006 01:37:48 -0800 Subject: [PATCH] mempool: add kzalloc allocator Add another allocator to the common mempool code: a kzalloc/kfree allocator This will be used by the next patch in the series to replace a mempool-backed kzalloc allocator. It is also very likely that there will be more users in the future. Signed-off-by: Matthew Dobson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mempool.h | 10 ++++++++-- mm/mempool.c | 9 ++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/mempool.h b/include/linux/mempool.h index d23dbf076b57..41570ce353eb 100644 --- a/include/linux/mempool.h +++ b/include/linux/mempool.h @@ -39,16 +39,22 @@ void *mempool_alloc_slab(gfp_t gfp_mask, void *pool_data); void mempool_free_slab(void *element, void *pool_data); /* - * A mempool_alloc_t and mempool_free_t to kmalloc the amount of memory - * specified by pool_data + * 2 mempool_alloc_t's and a mempool_free_t to kmalloc/kzalloc and kfree + * the amount of memory specified by pool_data */ void *mempool_kmalloc(gfp_t gfp_mask, void *pool_data); +void *mempool_kzalloc(gfp_t gfp_mask, void *pool_data); void mempool_kfree(void *element, void *pool_data); static inline mempool_t *mempool_create_kmalloc_pool(int min_nr, size_t size) { return mempool_create(min_nr, mempool_kmalloc, mempool_kfree, (void *) size); } +static inline mempool_t *mempool_create_kzalloc_pool(int min_nr, size_t size) +{ + return mempool_create(min_nr, mempool_kzalloc, mempool_kfree, + (void *) size); +} /* * A mempool_alloc_t and mempool_free_t for a simple page allocator that diff --git a/mm/mempool.c b/mm/mempool.c index a1397c6a4864..7bf064e6a345 100644 --- a/mm/mempool.c +++ b/mm/mempool.c @@ -296,11 +296,18 @@ EXPORT_SYMBOL(mempool_free_slab); */ void *mempool_kmalloc(gfp_t gfp_mask, void *pool_data) { - size_t size = (size_t) pool_data; + size_t size = (size_t)(long)pool_data; return kmalloc(size, gfp_mask); } EXPORT_SYMBOL(mempool_kmalloc); +void *mempool_kzalloc(gfp_t gfp_mask, void *pool_data) +{ + size_t size = (size_t) pool_data; + return kzalloc(size, gfp_mask); +} +EXPORT_SYMBOL(mempool_kzalloc); + void mempool_kfree(void *element, void *pool_data) { kfree(element); -- cgit v1.2.3 From fec433aaaae32a02329ad7d71b0f3c91b7525077 Mon Sep 17 00:00:00 2001 From: Matthew Dobson Date: Sun, 26 Mar 2006 01:37:49 -0800 Subject: [PATCH] mempool: add mempool_create_slab_pool() Create a simple wrapper function for the common case of creating a slab-based mempool. Signed-off-by: Matthew Dobson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mempool.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/mempool.h b/include/linux/mempool.h index 41570ce353eb..9be484d11283 100644 --- a/include/linux/mempool.h +++ b/include/linux/mempool.h @@ -6,6 +6,8 @@ #include +struct kmem_cache; + typedef void * (mempool_alloc_t)(gfp_t gfp_mask, void *pool_data); typedef void (mempool_free_t)(void *element, void *pool_data); @@ -37,6 +39,12 @@ extern void mempool_free(void *element, mempool_t *pool); */ void *mempool_alloc_slab(gfp_t gfp_mask, void *pool_data); void mempool_free_slab(void *element, void *pool_data); +static inline mempool_t * +mempool_create_slab_pool(int min_nr, struct kmem_cache *kc) +{ + return mempool_create(min_nr, mempool_alloc_slab, mempool_free_slab, + (void *) kc); +} /* * 2 mempool_alloc_t's and a mempool_free_t to kmalloc/kzalloc and kfree -- cgit v1.2.3 From 93d2341c750cda0df48a6cc67b35fe25f1ec47df Mon Sep 17 00:00:00 2001 From: Matthew Dobson Date: Sun, 26 Mar 2006 01:37:50 -0800 Subject: [PATCH] mempool: use mempool_create_slab_pool() Modify well over a dozen mempool users to call mempool_create_slab_pool() rather than calling mempool_create() with extra arguments, saving about 30 lines of code and increasing readability. Signed-off-by: Matthew Dobson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- block/cfq-iosched.c | 2 +- drivers/block/aoe/aoeblk.c | 4 +--- drivers/md/dm-crypt.c | 3 +-- drivers/md/dm-mpath.c | 3 +-- drivers/md/dm-snap.c | 3 +-- drivers/md/dm.c | 6 ++---- drivers/md/kcopyd.c | 3 +-- drivers/message/i2o/i2o_block.c | 7 +++---- drivers/scsi/iscsi_tcp.c | 4 ++-- drivers/scsi/qla2xxx/qla_os.c | 3 +-- drivers/scsi/scsi_lib.c | 5 ++--- fs/bio.c | 7 ++----- fs/cifs/cifsfs.c | 18 ++++++------------ fs/jfs/jfs_metapage.c | 4 ++-- fs/nfs/read.c | 6 ++---- fs/nfs/write.c | 12 ++++-------- fs/xfs/linux-2.6/xfs_super.c | 5 ++--- include/linux/i2o.h | 4 +--- net/sunrpc/sched.c | 12 ++++-------- 19 files changed, 39 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index c4a0d5d8d7f0..bde40a6ae665 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -2191,7 +2191,7 @@ static int cfq_init_queue(request_queue_t *q, elevator_t *e) if (!cfqd->cfq_hash) goto out_cfqhash; - cfqd->crq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, crq_pool); + cfqd->crq_pool = mempool_create_slab_pool(BLKDEV_MIN_RQ, crq_pool); if (!cfqd->crq_pool) goto out_crqpool; diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c index 32fea55fac48..393b86a3dbf8 100644 --- a/drivers/block/aoe/aoeblk.c +++ b/drivers/block/aoe/aoeblk.c @@ -211,9 +211,7 @@ aoeblk_gdalloc(void *vp) return; } - d->bufpool = mempool_create(MIN_BUFS, - mempool_alloc_slab, mempool_free_slab, - buf_pool_cache); + d->bufpool = mempool_create_slab_pool(MIN_BUFS, buf_pool_cache); if (d->bufpool == NULL) { printk(KERN_ERR "aoe: aoeblk_gdalloc: cannot allocate bufpool " "for %ld.%ld\n", d->aoemajor, d->aoeminor); diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index d88b8eda3903..259e86f26549 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -616,8 +616,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) } } - cc->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab, - mempool_free_slab, _crypt_io_pool); + cc->io_pool = mempool_create_slab_pool(MIN_IOS, _crypt_io_pool); if (!cc->io_pool) { ti->error = PFX "Cannot allocate crypt io mempool"; goto bad3; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index f72a82fb9434..1816f30678ed 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -179,8 +179,7 @@ static struct multipath *alloc_multipath(void) m->queue_io = 1; INIT_WORK(&m->process_queued_ios, process_queued_ios, m); INIT_WORK(&m->trigger_event, trigger_event, m); - m->mpio_pool = mempool_create(MIN_IOS, mempool_alloc_slab, - mempool_free_slab, _mpio_cache); + m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache); if (!m->mpio_pool) { kfree(m); return NULL; diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index f3759dd7828e..7401540086df 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -1174,8 +1174,7 @@ static int __init dm_snapshot_init(void) goto bad4; } - pending_pool = mempool_create(128, mempool_alloc_slab, - mempool_free_slab, pending_cache); + pending_pool = mempool_create_slab_pool(128, pending_cache); if (!pending_pool) { DMERR("Couldn't create pending pool."); r = -ENOMEM; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 8c82373f7ff3..a64798ef481e 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -823,13 +823,11 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent) md->queue->unplug_fn = dm_unplug_all; md->queue->issue_flush_fn = dm_flush_all; - md->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab, - mempool_free_slab, _io_cache); + md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache); if (!md->io_pool) goto bad2; - md->tio_pool = mempool_create(MIN_IOS, mempool_alloc_slab, - mempool_free_slab, _tio_cache); + md->tio_pool = mempool_create_slab_pool(MIN_IOS, _tio_cache); if (!md->tio_pool) goto bad3; diff --git a/drivers/md/kcopyd.c b/drivers/md/kcopyd.c index 0d54e8b7d9de..9dcb2c8a3853 100644 --- a/drivers/md/kcopyd.c +++ b/drivers/md/kcopyd.c @@ -227,8 +227,7 @@ static int jobs_init(void) if (!_job_cache) return -ENOMEM; - _job_pool = mempool_create(MIN_JOBS, mempool_alloc_slab, - mempool_free_slab, _job_cache); + _job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache); if (!_job_pool) { kmem_cache_destroy(_job_cache); return -ENOMEM; diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index b09fb6307153..7d4c5497785b 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -1179,10 +1179,9 @@ static int __init i2o_block_init(void) goto exit; } - i2o_blk_req_pool.pool = mempool_create(I2O_BLOCK_REQ_MEMPOOL_SIZE, - mempool_alloc_slab, - mempool_free_slab, - i2o_blk_req_pool.slab); + i2o_blk_req_pool.pool = + mempool_create_slab_pool(I2O_BLOCK_REQ_MEMPOOL_SIZE, + i2o_blk_req_pool.slab); if (!i2o_blk_req_pool.pool) { osm_err("can't init request mempool\n"); rc = -ENOMEM; diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 7b82ff090d42..2068b66822b7 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -3200,8 +3200,8 @@ iscsi_r2tpool_alloc(struct iscsi_session *session) * Data-Out PDU's within R2T-sequence can be quite big; * using mempool */ - ctask->datapool = mempool_create(ISCSI_DTASK_DEFAULT_MAX, - mempool_alloc_slab, mempool_free_slab, taskcache); + ctask->datapool = mempool_create_slab_pool(ISCSI_DTASK_DEFAULT_MAX, + taskcache); if (ctask->datapool == NULL) { kfifo_free(ctask->r2tqueue); iscsi_pool_free(&ctask->r2tpool, (void**)ctask->r2ts); diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 029bbf461bb2..017729c59a49 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2154,8 +2154,7 @@ qla2x00_allocate_sp_pool(scsi_qla_host_t *ha) int rval; rval = QLA_SUCCESS; - ha->srb_mempool = mempool_create(SRB_MIN_REQ, mempool_alloc_slab, - mempool_free_slab, srb_cachep); + ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep); if (ha->srb_mempool == NULL) { qla_printk(KERN_INFO, ha, "Unable to allocate SRB mempool.\n"); rval = QLA_FUNCTION_FAILED; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index ede158d08d9d..8f010a314a3d 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1787,9 +1787,8 @@ int __init scsi_init_queue(void) sgp->name); } - sgp->pool = mempool_create(SG_MEMPOOL_SIZE, - mempool_alloc_slab, mempool_free_slab, - sgp->slab); + sgp->pool = mempool_create_slab_pool(SG_MEMPOOL_SIZE, + sgp->slab); if (!sgp->pool) { printk(KERN_ERR "SCSI: can't init sg mempool %s\n", sgp->name); diff --git a/fs/bio.c b/fs/bio.c index 377046d82945..eb8fbc53f2cd 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -1141,8 +1141,7 @@ static int biovec_create_pools(struct bio_set *bs, int pool_entries, int scale) if (i >= scale) pool_entries >>= 1; - *bvp = mempool_create(pool_entries, mempool_alloc_slab, - mempool_free_slab, bp->slab); + *bvp = mempool_create_slab_pool(pool_entries, bp->slab); if (!*bvp) return -ENOMEM; } @@ -1179,9 +1178,7 @@ struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size, int scale) if (!bs) return NULL; - bs->bio_pool = mempool_create(bio_pool_size, mempool_alloc_slab, - mempool_free_slab, bio_slab); - + bs->bio_pool = mempool_create_slab_pool(bio_pool_size, bio_slab); if (!bs->bio_pool) goto bad; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 221b3334b737..6b99b51d6694 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -738,10 +738,8 @@ cifs_init_request_bufs(void) cERROR(1,("cifs_min_rcv set to maximum (64)")); } - cifs_req_poolp = mempool_create(cifs_min_rcv, - mempool_alloc_slab, - mempool_free_slab, - cifs_req_cachep); + cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv, + cifs_req_cachep); if(cifs_req_poolp == NULL) { kmem_cache_destroy(cifs_req_cachep); @@ -771,10 +769,8 @@ cifs_init_request_bufs(void) cFYI(1,("cifs_min_small set to maximum (256)")); } - cifs_sm_req_poolp = mempool_create(cifs_min_small, - mempool_alloc_slab, - mempool_free_slab, - cifs_sm_req_cachep); + cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small, + cifs_sm_req_cachep); if(cifs_sm_req_poolp == NULL) { mempool_destroy(cifs_req_poolp); @@ -808,10 +804,8 @@ cifs_init_mids(void) if (cifs_mid_cachep == NULL) return -ENOMEM; - cifs_mid_poolp = mempool_create(3 /* a reasonable min simultan opers */, - mempool_alloc_slab, - mempool_free_slab, - cifs_mid_cachep); + /* 3 is a reasonable minimum number of simultaneous operations */ + cifs_mid_poolp = mempool_create_slab_pool(3, cifs_mid_cachep); if(cifs_mid_poolp == NULL) { kmem_cache_destroy(cifs_mid_cachep); return -ENOMEM; diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 8508043849f3..f28696f235c4 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -220,8 +220,8 @@ int __init metapage_init(void) if (metapage_cache == NULL) return -ENOMEM; - metapage_mempool = mempool_create(METAPOOL_MIN_PAGES, mempool_alloc_slab, - mempool_free_slab, metapage_cache); + metapage_mempool = mempool_create_slab_pool(METAPOOL_MIN_PAGES, + metapage_cache); if (metapage_mempool == NULL) { kmem_cache_destroy(metapage_cache); diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 3961524fd4ab..624ca7146b6b 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -663,10 +663,8 @@ int nfs_init_readpagecache(void) if (nfs_rdata_cachep == NULL) return -ENOMEM; - nfs_rdata_mempool = mempool_create(MIN_POOL_READ, - mempool_alloc_slab, - mempool_free_slab, - nfs_rdata_cachep); + nfs_rdata_mempool = mempool_create_slab_pool(MIN_POOL_READ, + nfs_rdata_cachep); if (nfs_rdata_mempool == NULL) return -ENOMEM; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 3f5225404c97..4cfada2cc09f 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1521,17 +1521,13 @@ int nfs_init_writepagecache(void) if (nfs_wdata_cachep == NULL) return -ENOMEM; - nfs_wdata_mempool = mempool_create(MIN_POOL_WRITE, - mempool_alloc_slab, - mempool_free_slab, - nfs_wdata_cachep); + nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE, + nfs_wdata_cachep); if (nfs_wdata_mempool == NULL) return -ENOMEM; - nfs_commit_mempool = mempool_create(MIN_POOL_COMMIT, - mempool_alloc_slab, - mempool_free_slab, - nfs_wdata_cachep); + nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT, + nfs_wdata_cachep); if (nfs_commit_mempool == NULL) return -ENOMEM; diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 8355faf8ffde..1884300417e3 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -375,9 +375,8 @@ xfs_init_zones(void) if (!xfs_ioend_zone) goto out_destroy_vnode_zone; - xfs_ioend_pool = mempool_create(4 * MAX_BUF_PER_PAGE, - mempool_alloc_slab, mempool_free_slab, - xfs_ioend_zone); + xfs_ioend_pool = mempool_create_slab_pool(4 * MAX_BUF_PER_PAGE, + xfs_ioend_zone); if (!xfs_ioend_pool) goto out_free_ioend_zone; return 0; diff --git a/include/linux/i2o.h b/include/linux/i2o.h index 5a9d8c599171..dd7d627bf66f 100644 --- a/include/linux/i2o.h +++ b/include/linux/i2o.h @@ -950,9 +950,7 @@ static inline int i2o_pool_alloc(struct i2o_pool *pool, const char *name, if (!pool->slab) goto free_name; - pool->mempool = - mempool_create(min_nr, mempool_alloc_slab, mempool_free_slab, - pool->slab); + pool->mempool = mempool_create_slab_pool(min_nr, pool->slab); if (!pool->mempool) goto free_slab; diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index b9969b91a9f7..5c3eee768504 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -1167,16 +1167,12 @@ rpc_init_mempool(void) NULL, NULL); if (!rpc_buffer_slabp) goto err_nomem; - rpc_task_mempool = mempool_create(RPC_TASK_POOLSIZE, - mempool_alloc_slab, - mempool_free_slab, - rpc_task_slabp); + rpc_task_mempool = mempool_create_slab_pool(RPC_TASK_POOLSIZE, + rpc_task_slabp); if (!rpc_task_mempool) goto err_nomem; - rpc_buffer_mempool = mempool_create(RPC_BUFFER_POOLSIZE, - mempool_alloc_slab, - mempool_free_slab, - rpc_buffer_slabp); + rpc_buffer_mempool = mempool_create_slab_pool(RPC_BUFFER_POOLSIZE, + rpc_buffer_slabp); if (!rpc_buffer_mempool) goto err_nomem; return 0; -- cgit v1.2.3 From abcb6c9fd13fc2ad7757b818924dc8109a0e3775 Mon Sep 17 00:00:00 2001 From: Takashi Sato Date: Sun, 26 Mar 2006 01:37:51 -0800 Subject: [PATCH] 2TB files: st_blocks is invalid when calling stat64 This patch series fixes the following problems on 32 bits architecture. o stat64 returns the lower 32 bits of blocks, although userland st_blocks has 64 bits, because i_blocks has only 32 bits. The ioctl with FIOQSIZE has the same problem. o As Dave Kleikamp said, making >2TB file on JFS results in writing an invalid block number to disk inode. The cause is the same as above too. o In generic quota code dquot_transfer(), the file usage is calculated from i_blocks via inode_get_bytes(). If the file is over 2TB, the change of usage is less than expected. The cause is the same as above too. o As Trond Myklebust said, statfs64's entries related to blocks are invalid on statfs64 for a network filesystem which has more than 2^32-1 blocks with CONFIG_LBD disabled. [PATCH 3/3] We made patches to fix problems that occur when handling a large filesystem and a large file. It was discussed on the mails titled "stat64 for over 2TB file returned invalid st_blocks". Signed-off-by: Takashi Sato Cc: Dave Kleikamp Cc: Jan Kara Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-i386/stat.h | 3 +-- include/asm-m68k/stat.h | 3 +-- include/asm-sh/stat.h | 8 +------- include/linux/fs.h | 2 +- include/linux/stat.h | 2 +- 5 files changed, 5 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/asm-i386/stat.h b/include/asm-i386/stat.h index b464f8020ec4..67eae78323ba 100644 --- a/include/asm-i386/stat.h +++ b/include/asm-i386/stat.h @@ -58,8 +58,7 @@ struct stat64 { long long st_size; unsigned long st_blksize; - unsigned long st_blocks; /* Number 512-byte blocks allocated. */ - unsigned long __pad4; /* future possible st_blocks high bits */ + unsigned long long st_blocks; /* Number 512-byte blocks allocated. */ unsigned long st_atime; unsigned long st_atime_nsec; diff --git a/include/asm-m68k/stat.h b/include/asm-m68k/stat.h index c4c402a45e21..dd38bc2e9f98 100644 --- a/include/asm-m68k/stat.h +++ b/include/asm-m68k/stat.h @@ -60,8 +60,7 @@ struct stat64 { long long st_size; unsigned long st_blksize; - unsigned long __pad4; /* future possible st_blocks high bits */ - unsigned long st_blocks; /* Number 512-byte blocks allocated. */ + unsigned long long st_blocks; /* Number 512-byte blocks allocated. */ unsigned long st_atime; unsigned long st_atime_nsec; diff --git a/include/asm-sh/stat.h b/include/asm-sh/stat.h index 914e3fcbbd37..6c41a60657f1 100644 --- a/include/asm-sh/stat.h +++ b/include/asm-sh/stat.h @@ -60,13 +60,7 @@ struct stat64 { long long st_size; unsigned long st_blksize; -#if defined(__BIG_ENDIAN__) - unsigned long __pad4; /* Future possible st_blocks hi bits */ - unsigned long st_blocks; /* Number 512-byte blocks allocated. */ -#else /* Must be little */ - unsigned long st_blocks; /* Number 512-byte blocks allocated. */ - unsigned long __pad4; /* Future possible st_blocks hi bits */ -#endif + unsigned long long st_blocks; /* Number 512-byte blocks allocated. */ unsigned long st_atime; unsigned long st_atime_nsec; diff --git a/include/linux/fs.h b/include/linux/fs.h index ab67181a5a55..64b0ca4f14e3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -490,7 +490,7 @@ struct inode { unsigned int i_blkbits; unsigned long i_blksize; unsigned long i_version; - unsigned long i_blocks; + sector_t i_blocks; unsigned short i_bytes; spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ struct mutex i_mutex; diff --git a/include/linux/stat.h b/include/linux/stat.h index 8ff2a122dfef..8669291352db 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -69,7 +69,7 @@ struct kstat { struct timespec mtime; struct timespec ctime; unsigned long blksize; - unsigned long blocks; + unsigned long long blocks; }; #endif -- cgit v1.2.3 From a0f62ac6362c168754cccb36f196b3dfbddc3bc3 Mon Sep 17 00:00:00 2001 From: Takashi Sato Date: Sun, 26 Mar 2006 01:37:52 -0800 Subject: [PATCH] 2TB files: add blkcnt_t Add blkcnt_t as the type of inode.i_blocks. This enables you to make the size of blkcnt_t either 4 bytes or 8 bytes on 32 bits architecture with CONFIG_LSF. - CONFIG_LSF Add new configuration parameter. - blkcnt_t On h8300, i386, mips, powerpc, s390 and sh that define sector_t, blkcnt_t is defined as u64 if CONFIG_LSF is enabled; otherwise it is defined as unsigned long. On other architectures, it is defined as unsigned long. - inode.i_blocks Change the type from sector_t to blkcnt_t. Signed-off-by: Takashi Sato Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- block/Kconfig | 9 +++++++++ include/asm-h8300/types.h | 3 +++ include/asm-i386/types.h | 5 +++++ include/asm-mips/types.h | 5 +++++ include/asm-powerpc/types.h | 5 +++++ include/asm-s390/types.h | 5 +++++ include/asm-sh/types.h | 5 +++++ include/linux/fs.h | 2 +- include/linux/types.h | 4 ++++ 9 files changed, 42 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/block/Kconfig b/block/Kconfig index 96783645092d..43ca070dc0f8 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -23,4 +23,13 @@ config BLK_DEV_IO_TRACE git://brick.kernel.dk/data/git/blktrace.git +config LSF + bool "Support for Large Single Files" + depends on X86 || (MIPS && 32BIT) || PPC32 || ARCH_S390_31 || SUPERH || UML + default n + help + When CONFIG_LBD is disabled, say Y here if you want to + handle large file(bigger than 2TB), otherwise say N. + When CONFIG_LBD is enabled, Y is set automatically. + source block/Kconfig.iosched diff --git a/include/asm-h8300/types.h b/include/asm-h8300/types.h index bf91e0d4dde7..da2402b86540 100644 --- a/include/asm-h8300/types.h +++ b/include/asm-h8300/types.h @@ -58,6 +58,9 @@ typedef u32 dma_addr_t; #define HAVE_SECTOR_T typedef u64 sector_t; +#define HAVE_BLKCNT_T +typedef u64 blkcnt_t; + #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ diff --git a/include/asm-i386/types.h b/include/asm-i386/types.h index ced00fe8fe61..e50a08bd7ced 100644 --- a/include/asm-i386/types.h +++ b/include/asm-i386/types.h @@ -63,6 +63,11 @@ typedef u64 sector_t; #define HAVE_SECTOR_T #endif +#ifdef CONFIG_LSF +typedef u64 blkcnt_t; +#define HAVE_BLKCNT_T +#endif + #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/include/asm-mips/types.h b/include/asm-mips/types.h index 421b3aea14cc..cd2813d8e136 100644 --- a/include/asm-mips/types.h +++ b/include/asm-mips/types.h @@ -99,6 +99,11 @@ typedef u64 sector_t; #define HAVE_SECTOR_T #endif +#ifdef CONFIG_LSF +typedef u64 blkcnt_t; +#define HAVE_BLKCNT_T +#endif + #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/types.h b/include/asm-powerpc/types.h index ec3c2ee8bf86..baabba96e313 100644 --- a/include/asm-powerpc/types.h +++ b/include/asm-powerpc/types.h @@ -103,6 +103,11 @@ typedef u64 sector_t; #define HAVE_SECTOR_T #endif +#ifdef CONFIG_LSF +typedef u64 blkcnt_t; +#define HAVE_BLKCNT_T +#endif + #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/include/asm-s390/types.h b/include/asm-s390/types.h index d0be3e477013..5738ad63537c 100644 --- a/include/asm-s390/types.h +++ b/include/asm-s390/types.h @@ -93,6 +93,11 @@ typedef u64 sector_t; #define HAVE_SECTOR_T #endif +#ifdef CONFIG_LSF +typedef u64 blkcnt_t; +#define HAVE_BLKCNT_T +#endif + #endif /* ! __s390x__ */ #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/include/asm-sh/types.h b/include/asm-sh/types.h index cb7e183a0a6b..488552f43b2a 100644 --- a/include/asm-sh/types.h +++ b/include/asm-sh/types.h @@ -58,6 +58,11 @@ typedef u64 sector_t; #define HAVE_SECTOR_T #endif +#ifdef CONFIG_LSF +typedef u64 blkcnt_t; +#define HAVE_BLKCNT_T +#endif + #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 64b0ca4f14e3..155d29d5e5e4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -490,7 +490,7 @@ struct inode { unsigned int i_blkbits; unsigned long i_blksize; unsigned long i_version; - sector_t i_blocks; + blkcnt_t i_blocks; unsigned short i_bytes; spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ struct mutex i_mutex; diff --git a/include/linux/types.h b/include/linux/types.h index 54ae2d59e71b..1046c7ad86d9 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -137,6 +137,10 @@ typedef __s64 int64_t; typedef unsigned long sector_t; #endif +#ifndef HAVE_BLKCNT_T +typedef unsigned long blkcnt_t; +#endif + /* * The type of an index into the pagecache. Use a #define so asm/types.h * can override it. -- cgit v1.2.3 From e2d53f9525790dfacbcf09f359536311d3913d98 Mon Sep 17 00:00:00 2001 From: Takashi Sato Date: Sun, 26 Mar 2006 01:37:54 -0800 Subject: [PATCH] 2TB files: change type of kstatfs entries This fix was proposed by Trond Myklebust. He says: The type "sector_t" is heavily tied in to the block layer interface as an offset/handle to a block, and is subject to a supposedly block-specific configuration option: CONFIG_LBD. Despite this, it is used in struct kstatfs to save a couple of bytes on the stack whenever we call the filesystems' ->statfs(). So kstatfs's entries related to blocks are invalid on statfs64 for a network filesystem which has more than 2^32-1 blocks when CONFIG_LBD is disabled. - struct kstatfs Change the type of following entries from sector_t to u64. f_blocks f_bfree f_bavail f_files f_ffree Signed-off-by: Trond Myklebust Signed-off-by: Takashi Sato Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/statfs.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/statfs.h b/include/linux/statfs.h index ad83a2bdb821..b34cc829f98d 100644 --- a/include/linux/statfs.h +++ b/include/linux/statfs.h @@ -8,11 +8,11 @@ struct kstatfs { long f_type; long f_bsize; - sector_t f_blocks; - sector_t f_bfree; - sector_t f_bavail; - sector_t f_files; - sector_t f_ffree; + u64 f_blocks; + u64 f_bfree; + u64 f_bavail; + u64 f_files; + u64 f_ffree; __kernel_fsid_t f_fsid; long f_namelen; long f_frsize; -- cgit v1.2.3 From 89747d369d34e333b9b60f10f333a0b727b4e4e2 Mon Sep 17 00:00:00 2001 From: Mingming Cao Date: Sun, 26 Mar 2006 01:37:55 -0800 Subject: [PATCH] ext3_get_blocks: Mapping multiple blocks at a once Currently ext3_get_block() only maps or allocates one block at a time. This is quite inefficient for sequential IO workload. I have posted a early implements a simply multiple block map and allocation with current ext3. The basic idea is allocating the 1st block in the existing way, and attempting to allocate the next adjacent blocks on a best effort basis. More description about the implementation could be found here: http://marc.theaimsgroup.com/?l=ext2-devel&m=112162230003522&w=2 The following the latest version of the patch: break the original patch into 5 patches, re-worked some logicals, and fixed some bugs. The break ups are: [patch 1] Adding map multiple blocks at a time in ext3_get_blocks() [patch 2] Extend ext3_get_blocks() to support multiple block allocation [patch 3] Implement multiple block allocation in ext3-try-to-allocate (called via ext3_new_block()). [patch 4] Proper accounting updates in ext3_new_blocks() [patch 5] Adjust reservation window size properly (by the given number of blocks to allocate) before block allocation to increase the possibility of allocating multiple blocks in a single call. Tests done so far includes fsx,tiobench and dbench. The following numbers collected from Direct IO tests (1G file creation/read) shows the system time have been greatly reduced (more than 50% on my 8 cpu system) with the patches. 1G file DIO write: 2.6.15 2.6.15+patches real 0m31.275s 0m31.161s user 0m0.000s 0m0.000s sys 0m3.384s 0m0.564s 1G file DIO read: 2.6.15 2.6.15+patches real 0m30.733s 0m30.624s user 0m0.000s 0m0.004s sys 0m0.748s 0m0.380s Some previous test we did on buffered IO with using multiple blocks allocation and delayed allocation shows noticeable improvement on throughput and system time. This patch: Add support of mapping multiple blocks in one call. This is useful for DIO reads and re-writes (where blocks are already allocated), also is in line with Christoph's proposal of using getblocks() in mpage_readpage() or mpage_readpages(). Signed-off-by: Mingming Cao Cc: Badari Pulavarty Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext3/dir.c | 5 ++- fs/ext3/inode.c | 105 +++++++++++++++++++++++++++++++++++------------- include/linux/ext3_fs.h | 6 +-- 3 files changed, 82 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c index 773459164bb2..38bd3f6ec147 100644 --- a/fs/ext3/dir.c +++ b/fs/ext3/dir.c @@ -131,8 +131,9 @@ static int ext3_readdir(struct file * filp, struct buffer_head *bh = NULL; map_bh.b_state = 0; - err = ext3_get_block_handle(NULL, inode, blk, &map_bh, 0, 0); - if (!err) { + err = ext3_get_blocks_handle(NULL, inode, blk, 1, + &map_bh, 0, 0); + if (err > 0) { page_cache_readahead(sb->s_bdev->bd_inode->i_mapping, &filp->f_ra, filp, diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 76e22c9c9c6c..fcfb10f77120 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -330,7 +330,7 @@ static int ext3_block_to_path(struct inode *inode, ext3_warning (inode->i_sb, "ext3_block_to_path", "block > big"); } if (boundary) - *boundary = (i_block & (ptrs - 1)) == (final - 1); + *boundary = final - 1 - (i_block & (ptrs - 1)); return n; } @@ -669,11 +669,15 @@ err_out: * akpm: `handle' can be NULL if create == 0. * * The BKL may not be held on entry here. Be sure to take it early. + * return > 0, # of blocks mapped or allocated. + * return = 0, if plain lookup failed. + * return < 0, error case. */ int -ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create, int extend_disksize) +ext3_get_blocks_handle(handle_t *handle, struct inode *inode, sector_t iblock, + unsigned long maxblocks, struct buffer_head *bh_result, + int create, int extend_disksize) { int err = -EIO; int offsets[4]; @@ -681,11 +685,15 @@ ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock, Indirect *partial; unsigned long goal; int left; - int boundary = 0; - const int depth = ext3_block_to_path(inode, iblock, offsets, &boundary); + int blocks_to_boundary = 0; + int depth; struct ext3_inode_info *ei = EXT3_I(inode); + int count = 0; + unsigned long first_block = 0; + J_ASSERT(handle != NULL || create == 0); + depth = ext3_block_to_path(inode, iblock, offsets, &blocks_to_boundary); if (depth == 0) goto out; @@ -694,8 +702,31 @@ ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock, /* Simplest case - block found, no allocation needed */ if (!partial) { + first_block = chain[depth - 1].key; clear_buffer_new(bh_result); - goto got_it; + count++; + /*map more blocks*/ + while (count < maxblocks && count <= blocks_to_boundary) { + if (!verify_chain(chain, partial)) { + /* + * Indirect block might be removed by + * truncate while we were reading it. + * Handling of that case: forget what we've + * got now. Flag the err as EAGAIN, so it + * will reread. + */ + err = -EAGAIN; + count = 0; + break; + } + if (le32_to_cpu(*(chain[depth-1].p+count) == + (first_block + count))) + count++; + else + break; + } + if (err != -EAGAIN) + goto got_it; } /* Next simple case - plain lookup or failed read of indirect block */ @@ -723,6 +754,7 @@ ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock, } partial = ext3_get_branch(inode, depth, offsets, chain, &err); if (!partial) { + count++; mutex_unlock(&ei->truncate_mutex); if (err) goto cleanup; @@ -772,8 +804,9 @@ ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock, set_buffer_new(bh_result); got_it: map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); - if (boundary) + if (blocks_to_boundary == 0) set_buffer_boundary(bh_result); + err = count; /* Clean up and exit */ partial = chain + depth - 1; /* the whole chain */ cleanup: @@ -787,21 +820,6 @@ out: return err; } -static int ext3_get_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) -{ - handle_t *handle = NULL; - int ret; - - if (create) { - handle = ext3_journal_current_handle(); - J_ASSERT(handle != 0); - } - ret = ext3_get_block_handle(handle, inode, iblock, - bh_result, create, 1); - return ret; -} - #define DIO_CREDITS (EXT3_RESERVE_TRANS_BLOCKS + 32) static int @@ -812,9 +830,12 @@ ext3_direct_io_get_blocks(struct inode *inode, sector_t iblock, handle_t *handle = journal_current_handle(); int ret = 0; - if (!handle) + if (!create) goto get_block; /* A read */ + if (max_blocks == 1) + goto get_block; /* A single block get */ + if (handle->h_transaction->t_state == T_LOCKED) { /* * Huge direct-io writes can hold off commits for long @@ -841,13 +862,31 @@ ext3_direct_io_get_blocks(struct inode *inode, sector_t iblock, } get_block: - if (ret == 0) - ret = ext3_get_block_handle(handle, inode, iblock, - bh_result, create, 0); - bh_result->b_size = (1 << inode->i_blkbits); + if (ret == 0) { + ret = ext3_get_blocks_handle(handle, inode, iblock, + max_blocks, bh_result, create, 0); + if (ret > 0) { + bh_result->b_size = (ret << inode->i_blkbits); + ret = 0; + } + } return ret; } +static int ext3_get_blocks(struct inode *inode, sector_t iblock, + unsigned long maxblocks, struct buffer_head *bh_result, + int create) +{ + return ext3_direct_io_get_blocks(inode, iblock, maxblocks, + bh_result, create); +} + +static int ext3_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + return ext3_get_blocks(inode, iblock, 1, bh_result, create); +} + /* * `handle' can be NULL if create is zero */ @@ -862,8 +901,16 @@ struct buffer_head *ext3_getblk(handle_t *handle, struct inode * inode, dummy.b_state = 0; dummy.b_blocknr = -1000; buffer_trace_init(&dummy.b_history); - *errp = ext3_get_block_handle(handle, inode, block, &dummy, create, 1); - if (!*errp && buffer_mapped(&dummy)) { + err = ext3_get_blocks_handle(handle, inode, block, 1, + &dummy, create, 1); + if (err == 1) { + err = 0; + } else if (err >= 0) { + WARN_ON(1); + err = -EIO; + } + *errp = err; + if (!err && buffer_mapped(&dummy)) { struct buffer_head *bh; bh = sb_getblk(inode->i_sb, dummy.b_blocknr); if (!bh) { diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index e7239f2f97a1..0adadd85fa66 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -775,9 +775,9 @@ extern unsigned long ext3_count_free (struct buffer_head *, unsigned); int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int); struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *); struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *); -int ext3_get_block_handle(handle_t *handle, struct inode *inode, - sector_t iblock, struct buffer_head *bh_result, int create, - int extend_disksize); +int ext3_get_blocks_handle(handle_t *handle, struct inode *inode, + sector_t iblock, unsigned long maxblocks, struct buffer_head *bh_result, + int create, int extend_disksize); extern void ext3_read_inode (struct inode *); extern int ext3_write_inode (struct inode *, int); -- cgit v1.2.3 From b54e41ec17ae91dce174eb5a3515e7af4a440d42 Mon Sep 17 00:00:00 2001 From: Mingming Cao Date: Sun, 26 Mar 2006 01:37:57 -0800 Subject: [PATCH] ext3_get_blocks: support multiple blocks allocation in ext3_new_block() Change ext3_try_to_allocate() (called via ext3_new_blocks()) to try to allocate the requested number of blocks on a best effort basis: After allocated the first block, it will always attempt to allocate the next few(up to the requested size and not beyond the reservation window) adjacent blocks at the same time. Signed-off-by: Mingming Cao Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext3/balloc.c | 46 ++++++++++++++++++++++++++++++++++++---------- include/linux/ext3_fs.h | 5 ++++- 2 files changed, 40 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c index 46623f77666b..bba4aeb4a708 100644 --- a/fs/ext3/balloc.c +++ b/fs/ext3/balloc.c @@ -653,9 +653,11 @@ claim_block(spinlock_t *lock, int block, struct buffer_head *bh) */ static int ext3_try_to_allocate(struct super_block *sb, handle_t *handle, int group, - struct buffer_head *bitmap_bh, int goal, struct ext3_reserve_window *my_rsv) + struct buffer_head *bitmap_bh, int goal, + unsigned long *count, struct ext3_reserve_window *my_rsv) { int group_first_block, start, end; + unsigned long num = 0; /* we do allocation within the reservation window if we have a window */ if (my_rsv) { @@ -713,8 +715,18 @@ repeat: goto fail_access; goto repeat; } - return goal; + num++; + goal++; + while (num < *count && goal < end + && ext3_test_allocatable(goal, bitmap_bh) + && claim_block(sb_bgl_lock(EXT3_SB(sb), group), goal, bitmap_bh)) { + num++; + goal++; + } + *count = num; + return goal - num; fail_access: + *count = num; return -1; } @@ -1024,11 +1036,12 @@ static int ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle, unsigned int group, struct buffer_head *bitmap_bh, int goal, struct ext3_reserve_window_node * my_rsv, - int *errp) + unsigned long *count, int *errp) { unsigned long group_first_block; int ret = 0; int fatal; + unsigned long num = *count; *errp = 0; @@ -1051,7 +1064,8 @@ ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle, * or last attempt to allocate a block with reservation turned on failed */ if (my_rsv == NULL ) { - ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, goal, NULL); + ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, + goal, count, NULL); goto out; } /* @@ -1093,11 +1107,13 @@ ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle, || (my_rsv->rsv_end < group_first_block)) BUG(); ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, goal, - &my_rsv->rsv_window); + &num, &my_rsv->rsv_window); if (ret >= 0) { - my_rsv->rsv_alloc_hit++; + my_rsv->rsv_alloc_hit += num; + *count = num; break; /* succeed */ } + num = *count; } out: if (ret >= 0) { @@ -1154,8 +1170,8 @@ int ext3_should_retry_alloc(struct super_block *sb, int *retries) * bitmap, and then for any free bit if that fails. * This function also updates quota and i_blocks field. */ -int ext3_new_block(handle_t *handle, struct inode *inode, - unsigned long goal, int *errp) +int ext3_new_blocks(handle_t *handle, struct inode *inode, + unsigned long goal, unsigned long *count, int *errp) { struct buffer_head *bitmap_bh = NULL; struct buffer_head *gdp_bh; @@ -1178,6 +1194,7 @@ int ext3_new_block(handle_t *handle, struct inode *inode, static int goal_hits, goal_attempts; #endif unsigned long ngroups; + unsigned long num = *count; *errp = -ENOSPC; sb = inode->i_sb; @@ -1244,7 +1261,7 @@ retry: if (!bitmap_bh) goto io_error; ret_block = ext3_try_to_allocate_with_rsv(sb, handle, group_no, - bitmap_bh, ret_block, my_rsv, &fatal); + bitmap_bh, ret_block, my_rsv, &num, &fatal); if (fatal) goto out; if (ret_block >= 0) @@ -1281,7 +1298,7 @@ retry: if (!bitmap_bh) goto io_error; ret_block = ext3_try_to_allocate_with_rsv(sb, handle, group_no, - bitmap_bh, -1, my_rsv, &fatal); + bitmap_bh, -1, my_rsv, &num, &fatal); if (fatal) goto out; if (ret_block >= 0) @@ -1388,6 +1405,7 @@ allocated: *errp = 0; brelse(bitmap_bh); + *count = num; return ret_block; io_error: @@ -1406,6 +1424,14 @@ out: return 0; } +int ext3_new_block(handle_t *handle, struct inode *inode, + unsigned long goal, int *errp) +{ + unsigned long count = 1; + + return ext3_new_blocks(handle, inode, goal, &count, errp); +} + unsigned long ext3_count_free_blocks(struct super_block *sb) { unsigned long desc_count; diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 0adadd85fa66..8bb4f842cded 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -36,7 +36,8 @@ struct statfs; * Define EXT3_RESERVATION to reserve data blocks for expanding files */ #define EXT3_DEFAULT_RESERVE_BLOCKS 8 -#define EXT3_MAX_RESERVE_BLOCKS 1024 +/*max window size: 1024(direct blocks) + 3([t,d]indirect blocks) */ +#define EXT3_MAX_RESERVE_BLOCKS 1027 #define EXT3_RESERVE_WINDOW_NOT_ALLOCATED 0 /* * Always enable hashed directories @@ -732,6 +733,8 @@ struct dir_private_info { extern int ext3_bg_has_super(struct super_block *sb, int group); extern unsigned long ext3_bg_num_gdb(struct super_block *sb, int group); extern int ext3_new_block (handle_t *, struct inode *, unsigned long, int *); +extern int ext3_new_blocks (handle_t *, struct inode *, unsigned long, + unsigned long *, int *); extern void ext3_free_blocks (handle_t *, struct inode *, unsigned long, unsigned long); extern void ext3_free_blocks_sb (handle_t *, struct super_block *, -- cgit v1.2.3 From 205f87f6b342444f722e4559d33318686f7df2ca Mon Sep 17 00:00:00 2001 From: Badari Pulavarty Date: Sun, 26 Mar 2006 01:38:00 -0800 Subject: [PATCH] change buffer_head.b_size to size_t Increase the size of the buffer_head b_size field (only) for 64 bit platforms. Update some old and moldy comments in and around the structure as well. The b_size increase allows us to perform larger mappings and allocations for large I/O requests from userspace, which tie in with other changes allowing the get_block_t() interface to map multiple blocks at once. Signed-off-by: Nathan Scott Signed-off-by: Badari Pulavarty Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/buffer.c | 6 ++++-- fs/ocfs2/journal.c | 2 +- fs/reiserfs/prints.c | 2 +- include/linux/buffer_head.h | 19 +++++++++++-------- 4 files changed, 17 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/buffer.c b/fs/buffer.c index f25f58096428..e7a1461f4387 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -426,8 +426,10 @@ __find_get_block_slow(struct block_device *bdev, sector_t block) if (all_mapped) { printk("__find_get_block_slow() failed. " "block=%llu, b_blocknr=%llu\n", - (unsigned long long)block, (unsigned long long)bh->b_blocknr); - printk("b_state=0x%08lx, b_size=%u\n", bh->b_state, bh->b_size); + (unsigned long long)block, + (unsigned long long)bh->b_blocknr); + printk("b_state=0x%08lx, b_size=%zu\n", + bh->b_state, bh->b_size); printk("device blocksize: %d\n", 1 << bd_inode->i_blkbits); } out_unlock: diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 90641929a437..6a610ae53583 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -377,7 +377,7 @@ int ocfs2_journal_access(struct ocfs2_journal_handle *handle, BUG_ON(!bh); BUG_ON(!(handle->flags & OCFS2_HANDLE_STARTED)); - mlog_entry("bh->b_blocknr=%llu, type=%d (\"%s\"), bh->b_size = %hu\n", + mlog_entry("bh->b_blocknr=%llu, type=%d (\"%s\"), bh->b_size = %zu\n", (unsigned long long)bh->b_blocknr, type, (type == OCFS2_JOURNAL_ACCESS_CREATE) ? "OCFS2_JOURNAL_ACCESS_CREATE" : diff --git a/fs/reiserfs/prints.c b/fs/reiserfs/prints.c index 78b40621b88b..27bd3a1df2ad 100644 --- a/fs/reiserfs/prints.c +++ b/fs/reiserfs/prints.c @@ -143,7 +143,7 @@ static void sprintf_buffer_head(char *buf, struct buffer_head *bh) char b[BDEVNAME_SIZE]; sprintf(buf, - "dev %s, size %d, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)", + "dev %s, size %zd, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)", bdevname(bh->b_bdev, b), bh->b_size, (unsigned long long)bh->b_blocknr, atomic_read(&(bh->b_count)), bh->b_state, bh->b_page, diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index da917ed096a3..464f068f8b16 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -46,25 +46,28 @@ struct address_space; typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate); /* - * Keep related fields in common cachelines. The most commonly accessed - * field (b_state) goes at the start so the compiler does not generate - * indexed addressing for it. + * Historically, a buffer_head was used to map a single block + * within a page, and of course as the unit of I/O through the + * filesystem and block layers. Nowadays the basic I/O unit + * is the bio, and buffer_heads are used for extracting block + * mappings (via a get_block_t call), for tracking state within + * a page (via a page_mapping) and for wrapping bio submission + * for backward compatibility reasons (e.g. submit_bh). */ struct buffer_head { - /* First cache line: */ unsigned long b_state; /* buffer state bitmap (see above) */ struct buffer_head *b_this_page;/* circular list of page's buffers */ struct page *b_page; /* the page this bh is mapped to */ - atomic_t b_count; /* users using this block */ - u32 b_size; /* block size */ - sector_t b_blocknr; /* block number */ - char *b_data; /* pointer to data block */ + sector_t b_blocknr; /* start block number */ + size_t b_size; /* size of mapping */ + char *b_data; /* pointer to data within the page */ struct block_device *b_bdev; bh_end_io_t *b_end_io; /* I/O completion */ void *b_private; /* reserved for b_end_io */ struct list_head b_assoc_buffers; /* associated with another mapping */ + atomic_t b_count; /* users using this buffer_head */ }; /* -- cgit v1.2.3 From b0cf2321c6599138f860517745503691556d8453 Mon Sep 17 00:00:00 2001 From: Badari Pulavarty Date: Sun, 26 Mar 2006 01:38:00 -0800 Subject: [PATCH] pass b_size to ->get_block() Pass amount of disk needs to be mapped to get_block(). This way one can modify the fs ->get_block() functions to map multiple blocks at the same time. [akpm@osdl.org: performance tweak] [akpm@osdl.org: remove unneeded assignments] Signed-off-by: Badari Pulavarty Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/buffer.c | 9 ++++++++- fs/mpage.c | 2 ++ include/linux/buffer_head.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/buffer.c b/fs/buffer.c index e7a1461f4387..a507b58550f1 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1738,6 +1738,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, sector_t block; sector_t last_block; struct buffer_head *bh, *head; + const unsigned blocksize = 1 << inode->i_blkbits; int nr_underway = 0; BUG_ON(!PageLocked(page)); @@ -1745,7 +1746,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, last_block = (i_size_read(inode) - 1) >> inode->i_blkbits; if (!page_has_buffers(page)) { - create_empty_buffers(page, 1 << inode->i_blkbits, + create_empty_buffers(page, blocksize, (1 << BH_Dirty)|(1 << BH_Uptodate)); } @@ -1780,6 +1781,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, clear_buffer_dirty(bh); set_buffer_uptodate(bh); } else if (!buffer_mapped(bh) && buffer_dirty(bh)) { + WARN_ON(bh->b_size != blocksize); err = get_block(inode, block, bh, 1); if (err) goto recover; @@ -1933,6 +1935,7 @@ static int __block_prepare_write(struct inode *inode, struct page *page, if (buffer_new(bh)) clear_buffer_new(bh); if (!buffer_mapped(bh)) { + WARN_ON(bh->b_size != blocksize); err = get_block(inode, block, bh, 1); if (err) break; @@ -2088,6 +2091,7 @@ int block_read_full_page(struct page *page, get_block_t *get_block) fully_mapped = 0; if (iblock < lblock) { + WARN_ON(bh->b_size != blocksize); err = get_block(inode, iblock, bh, 0); if (err) SetPageError(page); @@ -2409,6 +2413,7 @@ int nobh_prepare_write(struct page *page, unsigned from, unsigned to, create = 1; if (block_start >= to) create = 0; + map_bh.b_size = blocksize; ret = get_block(inode, block_in_file + block_in_page, &map_bh, create); if (ret) @@ -2669,6 +2674,7 @@ int block_truncate_page(struct address_space *mapping, err = 0; if (!buffer_mapped(bh)) { + WARN_ON(bh->b_size != blocksize); err = get_block(inode, iblock, bh, 0); if (err) goto unlock; @@ -2755,6 +2761,7 @@ sector_t generic_block_bmap(struct address_space *mapping, sector_t block, struct inode *inode = mapping->host; tmp.b_state = 0; tmp.b_blocknr = 0; + tmp.b_size = 1 << inode->i_blkbits; get_block(inode, block, &tmp, 0); return tmp.b_blocknr; } diff --git a/fs/mpage.c b/fs/mpage.c index e431cb3878d6..7903b740cc11 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -192,6 +192,7 @@ do_mpage_readpage(struct bio *bio, struct page *page, unsigned nr_pages, page_block++, block_in_file++) { bh.b_state = 0; if (block_in_file < last_block) { + bh.b_size = blocksize; if (get_block(inode, block_in_file, &bh, 0)) goto confused; } @@ -472,6 +473,7 @@ __mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, for (page_block = 0; page_block < blocks_per_page; ) { map_bh.b_state = 0; + map_bh.b_size = 1 << blkbits; if (get_block(inode, block_in_file, &map_bh, 1)) goto confused; if (buffer_new(&map_bh)) diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 464f068f8b16..fb7e9b7ccbe3 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -280,6 +280,7 @@ map_bh(struct buffer_head *bh, struct super_block *sb, sector_t block) set_buffer_mapped(bh); bh->b_bdev = sb->s_bdev; bh->b_blocknr = block; + bh->b_size = sb->s_blocksize; } /* -- cgit v1.2.3 From 1d8fa7a2b9a39d18727acc5c468e870df606c852 Mon Sep 17 00:00:00 2001 From: Badari Pulavarty Date: Sun, 26 Mar 2006 01:38:02 -0800 Subject: [PATCH] remove ->get_blocks() support Now that get_block() can handle mapping multiple disk blocks, no need to have ->get_blocks(). This patch removes fs specific ->get_blocks() added for DIO and makes it users use get_block() instead. Signed-off-by: Badari Pulavarty Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/block_dev.c | 3 ++- fs/direct-io.c | 27 ++++++++++++++------------- fs/ext2/inode.c | 14 +------------- fs/ext3/inode.c | 12 ++---------- fs/fat/inode.c | 2 +- fs/hfs/inode.c | 13 +------------ fs/hfsplus/inode.c | 13 +------------ fs/jfs/inode.c | 2 +- fs/ocfs2/aops.c | 2 +- fs/reiserfs/inode.c | 1 - fs/xfs/linux-2.6/xfs_aops.c | 6 +++--- include/linux/fs.h | 17 +++++++---------- 12 files changed, 34 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/fs/block_dev.c b/fs/block_dev.c index 9a451a9ffad4..5983d42df015 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -131,9 +131,10 @@ blkdev_get_block(struct inode *inode, sector_t iblock, static int blkdev_get_blocks(struct inode *inode, sector_t iblock, - unsigned long max_blocks, struct buffer_head *bh, int create) + struct buffer_head *bh, int create) { sector_t end_block = max_block(I_BDEV(inode)); + unsigned long max_blocks = bh->b_size >> inode->i_blkbits; if ((iblock + max_blocks) > end_block) { max_blocks = end_block - iblock; diff --git a/fs/direct-io.c b/fs/direct-io.c index 235ed8d1f11e..9d1d2aa73e42 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -86,12 +86,12 @@ struct dio { unsigned first_block_in_page; /* doesn't change, Used only once */ int boundary; /* prev block is at a boundary */ int reap_counter; /* rate limit reaping */ - get_blocks_t *get_blocks; /* block mapping function */ + get_block_t *get_block; /* block mapping function */ dio_iodone_t *end_io; /* IO completion function */ sector_t final_block_in_bio; /* current final block in bio + 1 */ sector_t next_block_for_io; /* next block to be put under IO, in dio_blocks units */ - struct buffer_head map_bh; /* last get_blocks() result */ + struct buffer_head map_bh; /* last get_block() result */ /* * Deferred addition of a page to the dio. These variables are @@ -211,9 +211,9 @@ static struct page *dio_get_page(struct dio *dio) /* * Called when all DIO BIO I/O has been completed - let the filesystem - * know, if it registered an interest earlier via get_blocks. Pass the + * know, if it registered an interest earlier via get_block. Pass the * private field of the map buffer_head so that filesystems can use it - * to hold additional state between get_blocks calls and dio_complete. + * to hold additional state between get_block calls and dio_complete. */ static void dio_complete(struct dio *dio, loff_t offset, ssize_t bytes) { @@ -493,7 +493,7 @@ static int dio_bio_reap(struct dio *dio) * The fs is allowed to map lots of blocks at once. If it wants to do that, * it uses the passed inode-relative block number as the file offset, as usual. * - * get_blocks() is passed the number of i_blkbits-sized blocks which direct_io + * get_block() is passed the number of i_blkbits-sized blocks which direct_io * has remaining to do. The fs should not map more than this number of blocks. * * If the fs has mapped a lot of blocks, it should populate bh->b_size to @@ -506,7 +506,7 @@ static int dio_bio_reap(struct dio *dio) * In the case of filesystem holes: the fs may return an arbitrarily-large * hole by returning an appropriate value in b_size and by clearing * buffer_mapped(). However the direct-io code will only process holes one - * block at a time - it will repeatedly call get_blocks() as it walks the hole. + * block at a time - it will repeatedly call get_block() as it walks the hole. */ static int get_more_blocks(struct dio *dio) { @@ -548,7 +548,8 @@ static int get_more_blocks(struct dio *dio) * at a higher level for inside-i_size block-instantiating * writes. */ - ret = (*dio->get_blocks)(dio->inode, fs_startblk, fs_count, + map_bh->b_size = fs_count << dio->blkbits; + ret = (*dio->get_block)(dio->inode, fs_startblk, map_bh, create); } return ret; @@ -783,11 +784,11 @@ static void dio_zero_block(struct dio *dio, int end) * happily perform page-sized but 512-byte aligned IOs. It is important that * blockdev IO be able to have fine alignment and large sizes. * - * So what we do is to permit the ->get_blocks function to populate bh.b_size + * So what we do is to permit the ->get_block function to populate bh.b_size * with the size of IO which is permitted at this offset and this i_blkbits. * * For best results, the blockdev should be set up with 512-byte i_blkbits and - * it should set b_size to PAGE_SIZE or more inside get_blocks(). This gives + * it should set b_size to PAGE_SIZE or more inside get_block(). This gives * fine alignment but still allows this function to work in PAGE_SIZE units. */ static int do_direct_IO(struct dio *dio) @@ -947,7 +948,7 @@ out: static ssize_t direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode, const struct iovec *iov, loff_t offset, unsigned long nr_segs, - unsigned blkbits, get_blocks_t get_blocks, dio_iodone_t end_io, + unsigned blkbits, get_block_t get_block, dio_iodone_t end_io, struct dio *dio) { unsigned long user_addr; @@ -969,7 +970,7 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode, dio->boundary = 0; dio->reap_counter = 0; - dio->get_blocks = get_blocks; + dio->get_block = get_block; dio->end_io = end_io; dio->map_bh.b_private = NULL; dio->final_block_in_bio = -1; @@ -1177,7 +1178,7 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode, ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_blocks_t get_blocks, dio_iodone_t end_io, + unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, int dio_lock_type) { int seg; @@ -1273,7 +1274,7 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, (end > i_size_read(inode))); retval = direct_io_worker(rw, iocb, inode, iov, offset, - nr_segs, blkbits, get_blocks, end_io, dio); + nr_segs, blkbits, get_block, end_io, dio); if (rw == READ && dio_lock_type == DIO_LOCKING) release_i_mutex = 0; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index a717837f272e..04af9c45dce2 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -667,18 +667,6 @@ static sector_t ext2_bmap(struct address_space *mapping, sector_t block) return generic_block_bmap(mapping,block,ext2_get_block); } -static int -ext2_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks, - struct buffer_head *bh_result, int create) -{ - int ret; - - ret = ext2_get_block(inode, iblock, bh_result, create); - if (ret == 0) - bh_result->b_size = (1 << inode->i_blkbits); - return ret; -} - static ssize_t ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs) @@ -687,7 +675,7 @@ ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, struct inode *inode = file->f_mapping->host; return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, - offset, nr_segs, ext2_get_blocks, NULL); + offset, nr_segs, ext2_get_block, NULL); } static int diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 34e5b0dc9168..0cd126176bbb 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -940,11 +940,11 @@ out: static int ext3_direct_io_get_blocks(struct inode *inode, sector_t iblock, - unsigned long max_blocks, struct buffer_head *bh_result, int create) { handle_t *handle = journal_current_handle(); int ret = 0; + unsigned max_blocks = bh_result->b_size >> inode->i_blkbits; if (!create) goto get_block; /* A read */ @@ -989,18 +989,10 @@ get_block: return ret; } -static int ext3_get_blocks(struct inode *inode, sector_t iblock, - unsigned long maxblocks, struct buffer_head *bh_result, - int create) -{ - return ext3_direct_io_get_blocks(inode, iblock, maxblocks, - bh_result, create); -} - static int ext3_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { - return ext3_get_blocks(inode, iblock, 1, bh_result, create); + return ext3_direct_io_get_blocks(inode, iblock, bh_result, create); } /* diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 297300fe81c2..404bfc9f7385 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -101,11 +101,11 @@ static int __fat_get_blocks(struct inode *inode, sector_t iblock, } static int fat_get_blocks(struct inode *inode, sector_t iblock, - unsigned long max_blocks, struct buffer_head *bh_result, int create) { struct super_block *sb = inode->i_sb; int err; + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; err = __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create); if (err) diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 39fd85b9b916..2c564701724f 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -98,17 +98,6 @@ static int hfs_releasepage(struct page *page, gfp_t mask) return res ? try_to_free_buffers(page) : 0; } -static int hfs_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks, - struct buffer_head *bh_result, int create) -{ - int ret; - - ret = hfs_get_block(inode, iblock, bh_result, create); - if (!ret) - bh_result->b_size = (1 << inode->i_blkbits); - return ret; -} - static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs) { @@ -116,7 +105,7 @@ static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb, struct inode *inode = file->f_dentry->d_inode->i_mapping->host; return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, - offset, nr_segs, hfs_get_blocks, NULL); + offset, nr_segs, hfs_get_block, NULL); } static int hfs_writepages(struct address_space *mapping, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 12ed2b7d046b..9fbe4d2aeece 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -93,17 +93,6 @@ static int hfsplus_releasepage(struct page *page, gfp_t mask) return res ? try_to_free_buffers(page) : 0; } -static int hfsplus_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks, - struct buffer_head *bh_result, int create) -{ - int ret; - - ret = hfsplus_get_block(inode, iblock, bh_result, create); - if (!ret) - bh_result->b_size = (1 << inode->i_blkbits); - return ret; -} - static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs) { @@ -111,7 +100,7 @@ static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb, struct inode *inode = file->f_dentry->d_inode->i_mapping->host; return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, - offset, nr_segs, hfsplus_get_blocks, NULL); + offset, nr_segs, hfsplus_get_block, NULL); } static int hfsplus_writepages(struct address_space *mapping, diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 7239ef339489..04eb78f1252e 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -302,7 +302,7 @@ static ssize_t jfs_direct_IO(int rw, struct kiocb *iocb, struct inode *inode = file->f_mapping->host; return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, - offset, nr_segs, jfs_get_blocks, NULL); + offset, nr_segs, jfs_get_block, NULL); } struct address_space_operations jfs_aops = { diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index bf931ba1d364..0d858d0b25be 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -540,7 +540,6 @@ bail: * fs_count, map_bh, dio->rw == WRITE); */ static int ocfs2_direct_IO_get_blocks(struct inode *inode, sector_t iblock, - unsigned long max_blocks, struct buffer_head *bh_result, int create) { int ret; @@ -548,6 +547,7 @@ static int ocfs2_direct_IO_get_blocks(struct inode *inode, sector_t iblock, u64 p_blkno; int contig_blocks; unsigned char blocksize_bits; + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; if (!inode || !bh_result) { mlog(ML_ERROR, "inode or bh_result is null\n"); diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 62e18c19b446..9857e50f85e7 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -466,7 +466,6 @@ static int reiserfs_get_block_create_0(struct inode *inode, sector_t block, direct_IO request. */ static int reiserfs_get_blocks_direct_io(struct inode *inode, sector_t iblock, - unsigned long max_blocks, struct buffer_head *bh_result, int create) { diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index a79b84f8b55c..c02f7c5b7462 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1319,12 +1319,12 @@ STATIC int xfs_get_blocks_direct( struct inode *inode, sector_t iblock, - unsigned long max_blocks, struct buffer_head *bh_result, int create) { - return __xfs_get_block(inode, iblock, max_blocks, bh_result, - create, 1, BMAPI_WRITE|BMAPI_DIRECT); + return __xfs_get_block(inode, iblock, + bh_result->b_size >> inode->i_blkbits, + bh_result, create, 1, BMAPI_WRITE|BMAPI_DIRECT); } STATIC void diff --git a/include/linux/fs.h b/include/linux/fs.h index 155d29d5e5e4..9d9674946956 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -252,9 +252,6 @@ extern void __init files_init(unsigned long); struct buffer_head; typedef int (get_block_t)(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create); -typedef int (get_blocks_t)(struct inode *inode, sector_t iblock, - unsigned long max_blocks, - struct buffer_head *bh_result, int create); typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, ssize_t bytes, void *private); @@ -1645,7 +1642,7 @@ static inline void do_generic_file_read(struct file * filp, loff_t *ppos, ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_blocks_t get_blocks, dio_iodone_t end_io, + unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, int lock_type); enum { @@ -1656,29 +1653,29 @@ enum { static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, - loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks, + loff_t offset, unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io) { return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, - nr_segs, get_blocks, end_io, DIO_LOCKING); + nr_segs, get_block, end_io, DIO_LOCKING); } static inline ssize_t blockdev_direct_IO_no_locking(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, - loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks, + loff_t offset, unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io) { return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, - nr_segs, get_blocks, end_io, DIO_NO_LOCKING); + nr_segs, get_block, end_io, DIO_NO_LOCKING); } static inline ssize_t blockdev_direct_IO_own_locking(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, - loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks, + loff_t offset, unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io) { return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, - nr_segs, get_blocks, end_io, DIO_OWN_LOCKING); + nr_segs, get_block, end_io, DIO_OWN_LOCKING); } extern struct file_operations generic_ro_fops; -- cgit v1.2.3 From 92127c7a45d4d167d9b015a5f9de6b41ed66f1d0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 26 Mar 2006 01:38:05 -0800 Subject: [PATCH] hrtimers: optimize softirq runqueues The hrtimer softirq is called from the timer softirq every tick. Retrieve the current time from xtime and wall_to_monotonic instead of calling base->get_time() for each timer base. Store the time in the base structure and provide a hook once clock source abstractions are in place and to keep the code open for new base clocks. Based on a patch from: Roman Zippel Signed-off-by: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hrtimer.h | 20 ++++++++++++-------- kernel/hrtimer.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 6401c31d6add..64e2754ca734 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -72,14 +72,16 @@ struct hrtimer { /** * struct hrtimer_base - the timer base for a specific clock * - * @index: clock type index for per_cpu support when moving a timer - * to a base on another cpu. - * @lock: lock protecting the base and associated timers - * @active: red black tree root node for the active timers - * @first: pointer to the timer node which expires first - * @resolution: the resolution of the clock, in nanoseconds - * @get_time: function to retrieve the current time of the clock - * @curr_timer: the timer which is executing a callback right now + * @index: clock type index for per_cpu support when moving a timer + * to a base on another cpu. + * @lock: lock protecting the base and associated timers + * @active: red black tree root node for the active timers + * @first: pointer to the timer node which expires first + * @resolution: the resolution of the clock, in nanoseconds + * @get_time: function to retrieve the current time of the clock + * @get_sofirq_time: function to retrieve the current time from the softirq + * @curr_timer: the timer which is executing a callback right now + * @softirq_time: the time when running the hrtimer queue in the softirq */ struct hrtimer_base { clockid_t index; @@ -88,7 +90,9 @@ struct hrtimer_base { struct rb_node *first; ktime_t resolution; ktime_t (*get_time)(void); + ktime_t (*get_softirq_time)(void); struct hrtimer *curr_timer; + ktime_t softirq_time; }; /* diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 14bc9cfa6399..b728cc53452b 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -122,6 +122,26 @@ void ktime_get_ts(struct timespec *ts) } EXPORT_SYMBOL_GPL(ktime_get_ts); +/* + * Get the coarse grained time at the softirq based on xtime and + * wall_to_monotonic. + */ +static void hrtimer_get_softirq_time(struct hrtimer_base *base) +{ + ktime_t xtim, tomono; + unsigned long seq; + + do { + seq = read_seqbegin(&xtime_lock); + xtim = timespec_to_ktime(xtime); + tomono = timespec_to_ktime(wall_to_monotonic); + + } while (read_seqretry(&xtime_lock, seq)); + + base[CLOCK_REALTIME].softirq_time = xtim; + base[CLOCK_MONOTONIC].softirq_time = ktime_add(xtim, tomono); +} + /* * Functions and macros which are different for UP/SMP systems are kept in a * single place @@ -586,9 +606,11 @@ int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp) */ static inline void run_hrtimer_queue(struct hrtimer_base *base) { - ktime_t now = base->get_time(); struct rb_node *node; + if (base->get_softirq_time) + base->softirq_time = base->get_softirq_time(); + spin_lock_irq(&base->lock); while ((node = base->first)) { @@ -598,7 +620,7 @@ static inline void run_hrtimer_queue(struct hrtimer_base *base) void *data; timer = rb_entry(node, struct hrtimer, node); - if (now.tv64 <= timer->expires.tv64) + if (base->softirq_time.tv64 <= timer->expires.tv64) break; fn = timer->function; @@ -641,6 +663,8 @@ void hrtimer_run_queues(void) struct hrtimer_base *base = __get_cpu_var(hrtimer_bases); int i; + hrtimer_get_softirq_time(base); + for (i = 0; i < MAX_HRTIMER_BASES; i++) run_hrtimer_queue(&base[i]); } -- cgit v1.2.3 From 44f21475511bbc0135b52c66ad74dcc6a9026da3 Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Sun, 26 Mar 2006 01:38:06 -0800 Subject: [PATCH] hrtimers: pass current time to hrtimer_forward() Pass current time to hrtimer_forward(). This allows to use the softirq time in the timer base when the forward function is called from the timer callback. Other places pass current time with a call to timer->base->get_time(). Signed-off-by: Roman Zippel Signed-off-by: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hrtimer.h | 3 ++- kernel/hrtimer.c | 7 +++---- kernel/itimer.c | 3 ++- kernel/posix-timers.c | 14 ++++++++++---- 4 files changed, 17 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 64e2754ca734..84fc186324e6 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -130,7 +130,8 @@ static inline int hrtimer_active(const struct hrtimer *timer) } /* Forward a hrtimer so it expires after now: */ -extern unsigned long hrtimer_forward(struct hrtimer *timer, ktime_t interval); +extern unsigned long +hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval); /* Precise sleep: */ extern long hrtimer_nanosleep(struct timespec *rqtp, diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index b728cc53452b..e989c9981a96 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -301,18 +301,17 @@ void unlock_hrtimer_base(const struct hrtimer *timer, unsigned long *flags) * hrtimer_forward - forward the timer expiry * * @timer: hrtimer to forward + * @now: forward past this time * @interval: the interval to forward * * Forward the timer expiry so it will expire in the future. * Returns the number of overruns. */ unsigned long -hrtimer_forward(struct hrtimer *timer, ktime_t interval) +hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval) { unsigned long orun = 1; - ktime_t delta, now; - - now = timer->base->get_time(); + ktime_t delta; delta = ktime_sub(now, timer->expires); diff --git a/kernel/itimer.c b/kernel/itimer.c index 680e6b70c872..af2ec6b4392c 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -136,7 +136,8 @@ int it_real_fn(void *data) if (tsk->signal->it_real_incr.tv64 != 0) { hrtimer_forward(&tsk->signal->real_timer, - tsk->signal->it_real_incr); + tsk->signal->real_timer.base->softirq_time, + tsk->signal->it_real_incr); return HRTIMER_RESTART; } diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 9944379360b5..255657accf02 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -251,15 +251,18 @@ __initcall(init_posix_timers); static void schedule_next_timer(struct k_itimer *timr) { + struct hrtimer *timer = &timr->it.real.timer; + if (timr->it.real.interval.tv64 == 0) return; - timr->it_overrun += hrtimer_forward(&timr->it.real.timer, + timr->it_overrun += hrtimer_forward(timer, timer->base->get_time(), timr->it.real.interval); + timr->it_overrun_last = timr->it_overrun; timr->it_overrun = -1; ++timr->it_requeue_pending; - hrtimer_restart(&timr->it.real.timer); + hrtimer_restart(timer); } /* @@ -334,6 +337,7 @@ EXPORT_SYMBOL_GPL(posix_timer_event); static int posix_timer_fn(void *data) { struct k_itimer *timr = data; + struct hrtimer *timer = &timr->it.real.timer; unsigned long flags; int si_private = 0; int ret = HRTIMER_NORESTART; @@ -351,7 +355,8 @@ static int posix_timer_fn(void *data) */ if (timr->it.real.interval.tv64 != 0) { timr->it_overrun += - hrtimer_forward(&timr->it.real.timer, + hrtimer_forward(timer, + timer->base->softirq_time, timr->it.real.interval); ret = HRTIMER_RESTART; ++timr->it_requeue_pending; @@ -623,7 +628,8 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) if (timr->it_requeue_pending & REQUEUE_PENDING || (timr->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE) { timr->it_overrun += - hrtimer_forward(timer, timr->it.real.interval); + hrtimer_forward(timer, timer->base->get_time(), + timr->it.real.interval); remaining = hrtimer_get_remaining(timer); } calci: -- cgit v1.2.3 From 432569bb9d9d424d7ffe5b21f8205c55bdd1aaa8 Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Sun, 26 Mar 2006 01:38:08 -0800 Subject: [PATCH] hrtimers: simplify nanosleep nanosleep is the only user of the expired state, so let it manage this itself, which makes the hrtimer code a bit simpler. The remaining time is also only calculated if requested. Signed-off-by: Roman Zippel Acked-by: Ingo Molnar Acked-by: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hrtimer.h | 4 +- kernel/hrtimer.c | 142 +++++++++++++++++++++--------------------------- 2 files changed, 63 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 84fc186324e6..0e8f4762f6f8 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -38,9 +38,7 @@ enum hrtimer_restart { * Timer states: */ enum hrtimer_state { - HRTIMER_INACTIVE, /* Timer is inactive */ - HRTIMER_EXPIRED, /* Timer is expired */ - HRTIMER_RUNNING, /* Timer is running the callback function */ + HRTIMER_INACTIVE, /* Timer is inactive */ HRTIMER_PENDING, /* Timer is pending */ }; diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index e989c9981a96..59ec50c1e905 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -625,30 +625,20 @@ static inline void run_hrtimer_queue(struct hrtimer_base *base) fn = timer->function; data = timer->data; set_curr_timer(base, timer); - timer->state = HRTIMER_RUNNING; + timer->state = HRTIMER_INACTIVE; __remove_hrtimer(timer, base); spin_unlock_irq(&base->lock); - /* - * fn == NULL is special case for the simplest timer - * variant - wake up process and do not restart: - */ - if (!fn) { - wake_up_process(data); - restart = HRTIMER_NORESTART; - } else - restart = fn(data); + restart = fn(data); spin_lock_irq(&base->lock); /* Another CPU has added back the timer */ - if (timer->state != HRTIMER_RUNNING) + if (timer->state != HRTIMER_INACTIVE) continue; - if (restart == HRTIMER_RESTART) + if (restart != HRTIMER_NORESTART) enqueue_hrtimer(timer, base); - else - timer->state = HRTIMER_EXPIRED; } set_curr_timer(base, NULL); spin_unlock_irq(&base->lock); @@ -672,79 +662,70 @@ void hrtimer_run_queues(void) * Sleep related functions: */ -/** - * schedule_hrtimer - sleep until timeout - * - * @timer: hrtimer variable initialized with the correct clock base - * @mode: timeout value is abs/rel - * - * Make the current task sleep until @timeout is - * elapsed. - * - * You can set the task state as follows - - * - * %TASK_UNINTERRUPTIBLE - at least @timeout is guaranteed to - * pass before the routine returns. The routine will return 0 - * - * %TASK_INTERRUPTIBLE - the routine may return early if a signal is - * delivered to the current task. In this case the remaining time - * will be returned - * - * The current task state is guaranteed to be TASK_RUNNING when this - * routine returns. - */ -static ktime_t __sched -schedule_hrtimer(struct hrtimer *timer, const enum hrtimer_mode mode) -{ - /* fn stays NULL, meaning single-shot wakeup: */ - timer->data = current; +struct sleep_hrtimer { + struct hrtimer timer; + struct task_struct *task; + int expired; +}; - hrtimer_start(timer, timer->expires, mode); +static int nanosleep_wakeup(void *data) +{ + struct sleep_hrtimer *t = data; - schedule(); - hrtimer_cancel(timer); + t->expired = 1; + wake_up_process(t->task); - /* Return the remaining time: */ - if (timer->state != HRTIMER_EXPIRED) - return ktime_sub(timer->expires, timer->base->get_time()); - else - return (ktime_t) {.tv64 = 0 }; + return HRTIMER_NORESTART; } -static inline ktime_t __sched -schedule_hrtimer_interruptible(struct hrtimer *timer, - const enum hrtimer_mode mode) +static int __sched do_nanosleep(struct sleep_hrtimer *t, enum hrtimer_mode mode) { - set_current_state(TASK_INTERRUPTIBLE); + t->timer.function = nanosleep_wakeup; + t->timer.data = t; + t->task = current; + t->expired = 0; + + do { + set_current_state(TASK_INTERRUPTIBLE); + hrtimer_start(&t->timer, t->timer.expires, mode); + + schedule(); - return schedule_hrtimer(timer, mode); + if (unlikely(!t->expired)) { + hrtimer_cancel(&t->timer); + mode = HRTIMER_ABS; + } + } while (!t->expired && !signal_pending(current)); + + return t->expired; } static long __sched nanosleep_restart(struct restart_block *restart) { + struct sleep_hrtimer t; struct timespec __user *rmtp; struct timespec tu; - void *rfn_save = restart->fn; - struct hrtimer timer; - ktime_t rem; + ktime_t time; restart->fn = do_no_restart_syscall; - hrtimer_init(&timer, (clockid_t) restart->arg3, HRTIMER_ABS); - - timer.expires.tv64 = ((u64)restart->arg1 << 32) | (u64) restart->arg0; + hrtimer_init(&t.timer, restart->arg3, HRTIMER_ABS); + t.timer.expires.tv64 = ((u64)restart->arg1 << 32) | (u64) restart->arg0; - rem = schedule_hrtimer_interruptible(&timer, HRTIMER_ABS); - - if (rem.tv64 <= 0) + if (do_nanosleep(&t, HRTIMER_ABS)) return 0; rmtp = (struct timespec __user *) restart->arg2; - tu = ktime_to_timespec(rem); - if (rmtp && copy_to_user(rmtp, &tu, sizeof(tu))) - return -EFAULT; + if (rmtp) { + time = ktime_sub(t.timer.expires, t.timer.base->get_time()); + if (time.tv64 <= 0) + return 0; + tu = ktime_to_timespec(time); + if (copy_to_user(rmtp, &tu, sizeof(tu))) + return -EFAULT; + } - restart->fn = rfn_save; + restart->fn = nanosleep_restart; /* The other values in restart are already filled in */ return -ERESTART_RESTARTBLOCK; @@ -754,33 +735,34 @@ long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, const enum hrtimer_mode mode, const clockid_t clockid) { struct restart_block *restart; - struct hrtimer timer; + struct sleep_hrtimer t; struct timespec tu; ktime_t rem; - hrtimer_init(&timer, clockid, mode); - - timer.expires = timespec_to_ktime(*rqtp); - - rem = schedule_hrtimer_interruptible(&timer, mode); - if (rem.tv64 <= 0) + hrtimer_init(&t.timer, clockid, mode); + t.timer.expires = timespec_to_ktime(*rqtp); + if (do_nanosleep(&t, mode)) return 0; /* Absolute timers do not update the rmtp value and restart: */ if (mode == HRTIMER_ABS) return -ERESTARTNOHAND; - tu = ktime_to_timespec(rem); - - if (rmtp && copy_to_user(rmtp, &tu, sizeof(tu))) - return -EFAULT; + if (rmtp) { + rem = ktime_sub(t.timer.expires, t.timer.base->get_time()); + if (rem.tv64 <= 0) + return 0; + tu = ktime_to_timespec(rem); + if (copy_to_user(rmtp, &tu, sizeof(tu))) + return -EFAULT; + } restart = ¤t_thread_info()->restart_block; restart->fn = nanosleep_restart; - restart->arg0 = timer.expires.tv64 & 0xFFFFFFFF; - restart->arg1 = timer.expires.tv64 >> 32; + restart->arg0 = t.timer.expires.tv64 & 0xFFFFFFFF; + restart->arg1 = t.timer.expires.tv64 >> 32; restart->arg2 = (unsigned long) rmtp; - restart->arg3 = (unsigned long) timer.base->index; + restart->arg3 = (unsigned long) t.timer.base->index; return -ERESTART_RESTARTBLOCK; } -- cgit v1.2.3 From b75f7a51ca75c977d7d77f735d7a7859194eb39e Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Sun, 26 Mar 2006 01:38:09 -0800 Subject: [PATCH] hrtimers: remove state field Remove the state field and encode this information in the rb_node similiar to normal timer. Signed-off-by: Roman Zippel Acked-by: Ingo Molnar Signed-off-by: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hrtimer.h | 11 ++--------- kernel/hrtimer.c | 14 +++++--------- 2 files changed, 7 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 0e8f4762f6f8..f57cc7bd7008 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -34,13 +34,7 @@ enum hrtimer_restart { HRTIMER_RESTART, }; -/* - * Timer states: - */ -enum hrtimer_state { - HRTIMER_INACTIVE, /* Timer is inactive */ - HRTIMER_PENDING, /* Timer is pending */ -}; +#define HRTIMER_INACTIVE ((void *)1UL) struct hrtimer_base; @@ -61,7 +55,6 @@ struct hrtimer_base; struct hrtimer { struct rb_node node; ktime_t expires; - enum hrtimer_state state; int (*function)(void *); void *data; struct hrtimer_base *base; @@ -124,7 +117,7 @@ extern ktime_t hrtimer_get_next_event(void); static inline int hrtimer_active(const struct hrtimer *timer) { - return timer->state == HRTIMER_PENDING; + return timer->node.rb_parent != HRTIMER_INACTIVE; } /* Forward a hrtimer so it expires after now: */ diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 59ec50c1e905..658d49feedb9 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -374,8 +374,6 @@ static void enqueue_hrtimer(struct hrtimer *timer, struct hrtimer_base *base) rb_link_node(&timer->node, parent, link); rb_insert_color(&timer->node, &base->active); - timer->state = HRTIMER_PENDING; - if (!base->first || timer->expires.tv64 < rb_entry(base->first, struct hrtimer, node)->expires.tv64) base->first = &timer->node; @@ -395,6 +393,7 @@ static void __remove_hrtimer(struct hrtimer *timer, struct hrtimer_base *base) if (base->first == &timer->node) base->first = rb_next(&timer->node); rb_erase(&timer->node, &base->active); + timer->node.rb_parent = HRTIMER_INACTIVE; } /* @@ -405,7 +404,6 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_base *base) { if (hrtimer_active(timer)) { __remove_hrtimer(timer, base); - timer->state = HRTIMER_INACTIVE; return 1; } return 0; @@ -579,6 +577,7 @@ void hrtimer_init(struct hrtimer *timer, clockid_t clock_id, clock_id = CLOCK_MONOTONIC; timer->base = &bases[clock_id]; + timer->node.rb_parent = HRTIMER_INACTIVE; } /** @@ -625,7 +624,6 @@ static inline void run_hrtimer_queue(struct hrtimer_base *base) fn = timer->function; data = timer->data; set_curr_timer(base, timer); - timer->state = HRTIMER_INACTIVE; __remove_hrtimer(timer, base); spin_unlock_irq(&base->lock); @@ -633,12 +631,10 @@ static inline void run_hrtimer_queue(struct hrtimer_base *base) spin_lock_irq(&base->lock); - /* Another CPU has added back the timer */ - if (timer->state != HRTIMER_INACTIVE) - continue; - - if (restart != HRTIMER_NORESTART) + if (restart != HRTIMER_NORESTART) { + BUG_ON(hrtimer_active(timer)); enqueue_hrtimer(timer, base); + } } set_curr_timer(base, NULL); spin_unlock_irq(&base->lock); -- cgit v1.2.3 From 272705c5979c114e63dbfcd28ea15093038a4c42 Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Sun, 26 Mar 2006 01:38:10 -0800 Subject: [PATCH] hrtimers: remove DEFINE_KTIME and ktime_to_clock_t() Now that it_real_value is gone, the last user of DEFINE_KTIME and ktime_to_clock_t are also gone, so remove it before someone starts using it again. Signed-off-by: Roman Zippel Acked-by: Ingo Molnar Acked-by: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ktime.h | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'include') diff --git a/include/linux/ktime.h b/include/linux/ktime.h index f3dec45ef874..62bc57580707 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -64,9 +64,6 @@ typedef union { #if (BITS_PER_LONG == 64) || defined(CONFIG_KTIME_SCALAR) -/* Define a ktime_t variable and initialize it to zero: */ -#define DEFINE_KTIME(kt) ktime_t kt = { .tv64 = 0 } - /** * ktime_set - Set a ktime_t variable from a seconds/nanoseconds value * @@ -113,9 +110,6 @@ static inline ktime_t timeval_to_ktime(struct timeval tv) /* Map the ktime_t to timeval conversion to ns_to_timeval function */ #define ktime_to_timeval(kt) ns_to_timeval((kt).tv64) -/* Map the ktime_t to clock_t conversion to the inline in jiffies.h: */ -#define ktime_to_clock_t(kt) nsec_to_clock_t((kt).tv64) - /* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */ #define ktime_to_ns(kt) ((kt).tv64) @@ -136,9 +130,6 @@ static inline ktime_t timeval_to_ktime(struct timeval tv) * tv.sec < 0 and 0 >= tv.nsec < NSEC_PER_SEC */ -/* Define a ktime_t variable and initialize it to zero: */ -#define DEFINE_KTIME(kt) ktime_t kt = { .tv64 = 0 } - /* Set a ktime_t variable to a value in sec/nsec representation: */ static inline ktime_t ktime_set(const long secs, const unsigned long nsecs) { @@ -254,17 +245,6 @@ static inline struct timeval ktime_to_timeval(const ktime_t kt) .tv_usec = (suseconds_t) (kt.tv.nsec / NSEC_PER_USEC) }; } -/** - * ktime_to_clock_t - convert a ktime_t variable to clock_t format - * @kt: the ktime_t variable to convert - * - * Returns a clock_t variable with the converted value - */ -static inline clock_t ktime_to_clock_t(const ktime_t kt) -{ - return nsec_to_clock_t( (u64) kt.tv.sec * NSEC_PER_SEC + kt.tv.nsec); -} - /** * ktime_to_ns - convert a ktime_t variable to scalar nanoseconds * @kt: the ktime_t variable to convert -- cgit v1.2.3 From df869b630d9d9131c10cf073fb61646048874b2f Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Sun, 26 Mar 2006 01:38:11 -0800 Subject: [PATCH] hrtimers: remove nsec_t typedef nsec_t predates ktime_t and has mostly been superseded by it. In the few places that are left it's better to make it explicit that we're dealing with 64 bit values here. Signed-off-by: Roman Zippel Acked-by: Thomas Gleixner Acked-by: John Stultz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/time.h | 18 ++++++------------ kernel/hrtimer.c | 4 ++-- kernel/time.c | 4 ++-- 3 files changed, 10 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/time.h b/include/linux/time.h index bf0e785e2e03..0cd696cee998 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -73,12 +73,6 @@ extern void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec); #define timespec_valid(ts) \ (((ts)->tv_sec >= 0) && (((unsigned long) (ts)->tv_nsec) < NSEC_PER_SEC)) -/* - * 64-bit nanosec type. Large enough to span 292+ years in nanosecond - * resolution. Ought to be enough for a while. - */ -typedef s64 nsec_t; - extern struct timespec xtime; extern struct timespec wall_to_monotonic; extern seqlock_t xtime_lock; @@ -114,9 +108,9 @@ extern struct timespec timespec_trunc(struct timespec t, unsigned gran); * Returns the scalar nanosecond representation of the timespec * parameter. */ -static inline nsec_t timespec_to_ns(const struct timespec *ts) +static inline s64 timespec_to_ns(const struct timespec *ts) { - return ((nsec_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec; + return ((s64) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec; } /** @@ -126,9 +120,9 @@ static inline nsec_t timespec_to_ns(const struct timespec *ts) * Returns the scalar nanosecond representation of the timeval * parameter. */ -static inline nsec_t timeval_to_ns(const struct timeval *tv) +static inline s64 timeval_to_ns(const struct timeval *tv) { - return ((nsec_t) tv->tv_sec * NSEC_PER_SEC) + + return ((s64) tv->tv_sec * NSEC_PER_SEC) + tv->tv_usec * NSEC_PER_USEC; } @@ -138,7 +132,7 @@ static inline nsec_t timeval_to_ns(const struct timeval *tv) * * Returns the timespec representation of the nsec parameter. */ -extern struct timespec ns_to_timespec(const nsec_t nsec); +extern struct timespec ns_to_timespec(const s64 nsec); /** * ns_to_timeval - Convert nanoseconds to timeval @@ -146,7 +140,7 @@ extern struct timespec ns_to_timespec(const nsec_t nsec); * * Returns the timeval representation of the nsec parameter. */ -extern struct timeval ns_to_timeval(const nsec_t nsec); +extern struct timeval ns_to_timeval(const s64 nsec); #endif /* __KERNEL__ */ diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 658d49feedb9..44108de4f028 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -266,7 +266,7 @@ ktime_t ktime_add_ns(const ktime_t kt, u64 nsec) /* * Divide a ktime value by a nanosecond value */ -static unsigned long ktime_divns(const ktime_t kt, nsec_t div) +static unsigned long ktime_divns(const ktime_t kt, s64 div) { u64 dclc, inc, dns; int sft = 0; @@ -322,7 +322,7 @@ hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval) interval.tv64 = timer->base->resolution.tv64; if (unlikely(delta.tv64 >= interval.tv64)) { - nsec_t incr = ktime_to_ns(interval); + s64 incr = ktime_to_ns(interval); orun = ktime_divns(delta, incr); timer->expires = ktime_add_ns(timer->expires, incr * orun); diff --git a/kernel/time.c b/kernel/time.c index e00a97b77241..ff8e7019c4c4 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -610,7 +610,7 @@ void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec) * * Returns the timespec representation of the nsec parameter. */ -struct timespec ns_to_timespec(const nsec_t nsec) +struct timespec ns_to_timespec(const s64 nsec) { struct timespec ts; @@ -630,7 +630,7 @@ struct timespec ns_to_timespec(const nsec_t nsec) * * Returns the timeval representation of the nsec parameter. */ -struct timeval ns_to_timeval(const nsec_t nsec) +struct timeval ns_to_timeval(const s64 nsec) { struct timespec ts = ns_to_timespec(nsec); struct timeval tv; -- cgit v1.2.3 From 05cfb614ddbf3181540ce09d44d96486f8ba8d6a Mon Sep 17 00:00:00 2001 From: Roman Zippel Date: Sun, 26 Mar 2006 01:38:12 -0800 Subject: [PATCH] hrtimers: remove data field The nanosleep cleanup allows to remove the data field of hrtimer. The callback function can use container_of() to get it's own data. Since the hrtimer structure is anyway embedded in other structures, this adds no overhead. Signed-off-by: Roman Zippel Signed-off-by: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 2 +- include/linux/hrtimer.h | 5 +---- include/linux/sched.h | 1 + include/linux/timer.h | 3 ++- kernel/fork.c | 2 +- kernel/hrtimer.c | 12 +++++------- kernel/itimer.c | 15 +++++++-------- kernel/posix-timers.c | 9 ++++----- 8 files changed, 22 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index 995cba3c62b8..c7397c46ad6d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -632,7 +632,7 @@ static int de_thread(struct task_struct *tsk) * synchronize with any firing (by calling del_timer_sync) * before we can safely let the old group leader die. */ - sig->real_timer.data = current; + sig->tsk = current; spin_unlock_irq(lock); if (hrtimer_cancel(&sig->real_timer)) hrtimer_restart(&sig->real_timer); diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index f57cc7bd7008..93830158348e 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -45,9 +45,7 @@ struct hrtimer_base; * @expires: the absolute expiry time in the hrtimers internal * representation. The time is related to the clock on * which the timer is based. - * @state: state of the timer * @function: timer expiry callback function - * @data: argument for the callback function * @base: pointer to the timer base (per cpu and per clock) * * The hrtimer structure must be initialized by init_hrtimer_#CLOCKTYPE() @@ -55,8 +53,7 @@ struct hrtimer_base; struct hrtimer { struct rb_node node; ktime_t expires; - int (*function)(void *); - void *data; + int (*function)(struct hrtimer *); struct hrtimer_base *base; }; diff --git a/include/linux/sched.h b/include/linux/sched.h index e0054c1b9a09..036d14d2bf90 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -402,6 +402,7 @@ struct signal_struct { /* ITIMER_REAL timer for the process */ struct hrtimer real_timer; + struct task_struct *tsk; ktime_t it_real_incr; /* ITIMER_PROF and ITIMER_VIRTUAL timers for the process */ diff --git a/include/linux/timer.h b/include/linux/timer.h index ee5a09e806e8..b5caabca553c 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -96,6 +96,7 @@ static inline void add_timer(struct timer_list *timer) extern void init_timers(void); extern void run_local_timers(void); -extern int it_real_fn(void *); +struct hrtimer; +extern int it_real_fn(struct hrtimer *); #endif diff --git a/kernel/fork.c b/kernel/fork.c index a02063903aaa..4bd6486aa67d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -848,7 +848,7 @@ static inline int copy_signal(unsigned long clone_flags, struct task_struct * ts hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_REL); sig->it_real_incr.tv64 = 0; sig->real_timer.function = it_real_fn; - sig->real_timer.data = tsk; + sig->tsk = tsk; sig->it_virt_expires = cputime_zero; sig->it_virt_incr = cputime_zero; diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 44108de4f028..0237a556eb1f 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -613,21 +613,19 @@ static inline void run_hrtimer_queue(struct hrtimer_base *base) while ((node = base->first)) { struct hrtimer *timer; - int (*fn)(void *); + int (*fn)(struct hrtimer *); int restart; - void *data; timer = rb_entry(node, struct hrtimer, node); if (base->softirq_time.tv64 <= timer->expires.tv64) break; fn = timer->function; - data = timer->data; set_curr_timer(base, timer); __remove_hrtimer(timer, base); spin_unlock_irq(&base->lock); - restart = fn(data); + restart = fn(timer); spin_lock_irq(&base->lock); @@ -664,9 +662,10 @@ struct sleep_hrtimer { int expired; }; -static int nanosleep_wakeup(void *data) +static int nanosleep_wakeup(struct hrtimer *timer) { - struct sleep_hrtimer *t = data; + struct sleep_hrtimer *t = + container_of(timer, struct sleep_hrtimer, timer); t->expired = 1; wake_up_process(t->task); @@ -677,7 +676,6 @@ static int nanosleep_wakeup(void *data) static int __sched do_nanosleep(struct sleep_hrtimer *t, enum hrtimer_mode mode) { t->timer.function = nanosleep_wakeup; - t->timer.data = t; t->task = current; t->expired = 0; diff --git a/kernel/itimer.c b/kernel/itimer.c index af2ec6b4392c..204ed7939e75 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -128,17 +128,16 @@ asmlinkage long sys_getitimer(int which, struct itimerval __user *value) /* * The timer is automagically restarted, when interval != 0 */ -int it_real_fn(void *data) +int it_real_fn(struct hrtimer *timer) { - struct task_struct *tsk = (struct task_struct *) data; + struct signal_struct *sig = + container_of(timer, struct signal_struct, real_timer); - send_group_sig_info(SIGALRM, SEND_SIG_PRIV, tsk); - - if (tsk->signal->it_real_incr.tv64 != 0) { - hrtimer_forward(&tsk->signal->real_timer, - tsk->signal->real_timer.base->softirq_time, - tsk->signal->it_real_incr); + send_group_sig_info(SIGALRM, SEND_SIG_PRIV, sig->tsk); + if (sig->it_real_incr.tv64 != 0) { + hrtimer_forward(timer, timer->base->softirq_time, + sig->it_real_incr); return HRTIMER_RESTART; } return HRTIMER_NORESTART; diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 7c5f44787c8c..ac6dc8744429 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -145,7 +145,7 @@ static int common_timer_set(struct k_itimer *, int, struct itimerspec *, struct itimerspec *); static int common_timer_del(struct k_itimer *timer); -static int posix_timer_fn(void *data); +static int posix_timer_fn(struct hrtimer *data); static struct k_itimer *lock_timer(timer_t timer_id, unsigned long *flags); @@ -334,14 +334,14 @@ EXPORT_SYMBOL_GPL(posix_timer_event); * This code is for CLOCK_REALTIME* and CLOCK_MONOTONIC* timers. */ -static int posix_timer_fn(void *data) +static int posix_timer_fn(struct hrtimer *timer) { - struct k_itimer *timr = data; - struct hrtimer *timer = &timr->it.real.timer; + struct k_itimer *timr; unsigned long flags; int si_private = 0; int ret = HRTIMER_NORESTART; + timr = container_of(timer, struct k_itimer, it.real.timer); spin_lock_irqsave(&timr->it_lock, flags); if (timr->it.real.interval.tv64 != 0) @@ -725,7 +725,6 @@ common_timer_set(struct k_itimer *timr, int flags, mode = flags & TIMER_ABSTIME ? HRTIMER_ABS : HRTIMER_REL; hrtimer_init(&timr->it.real.timer, timr->it_clock, mode); - timr->it.real.timer.data = timr; timr->it.real.timer.function = posix_timer_fn; timer->expires = timespec_to_ktime(new_setting->it_value); -- cgit v1.2.3 From 311ac88fd2d4194a95e9e38d2fe08917be98723c Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Sun, 26 Mar 2006 01:38:17 -0800 Subject: [PATCH] x86: kprobes-booster Current kprobe copies the original instruction at the probe point and replaces it with a breakpoint instruction (int3). When the kernel hits the probe point, kprobe handler is invoked. And the copied instruction is single-step executed on the copied buffer (not on the original address) by kprobe. After that, the kprobe checks registers and modify it (if need) as if the instructions was executed on the original address. My proposal is based on the fact there are many instructions which do NOT require the register modification after the single-step execution. When the copied instruction is a kind of them, kprobe just jumps back to the next instruction after single-step execution. If so, why don't we execute those instructions directly? With kprobe-booster patch, kprobes will execute a copied instruction directly and (if need) jump back to original code. This direct execution is executed when the kprobe don't have both post_handler and break_handler, and the copied instruction can be executed directly. I sorted instructions which can be executed directly or not; - Call instructions are NG(can not be executed directly). We should correct the return address pushed into top of stack. - Indirect instructions except for absolute indirect-jumps are NG. Those instructions changes EIP randomly. We should check EIP and correct it. - Instructions that change EIP beyond the range of the instruction buffer are NG. - Instructions that change EIP to tail 5 bytes of the instruction buffer (it is the size of a jump instruction). We must write a jump instruction which backs to original kernel code in the instruction buffer. - Break point instruction is NG. We should not touch EIP and pass to other handlers. - Absolute direct/indirect jumps are OK.- Conditional Jumps are NG. - Halt and software-interruptions are NG. Because it will stay on the instruction buffer of kprobes. - Prefixes are NG. - Unknown/reserved opcode is NG. - Other 1 byte instructions are OK. But those instructions need a jump back code. - 2 bytes instructions are mapped sparsely. So, in this release, this patch don't boost those instructions. >From Intel's IA-32 opcode map described in IA-32 Intel Architecture Software Developer's Manual Vol.2 B, I determined that following opcodes are not boostable. - 0FH (2byte escape) - 70H - 7FH (Jump on condition) - 9AH (Call) and 9CH (Pushf) - C0H-C1H (Grp 2: includes reserved opcode) - C6H-C7H (Grp11: includes reserved opcode) - CCH-CEH (Software-interrupt) - D0H-D3H (Grp2: includes reserved opcode) - D6H (Reserved) - D8H-DFH (Coprocessor) - E0H-E3H (loop/conditional jump) - E8H (Call) - F0H-F3H (Prefixes and reserved) - F4H (Halt) - F6H-F7H (Grp3: includes reserved opcode) - FEH-FFH(Grp4,5: includes reserved opcode) Kprobe-booster checks whether target instruction can be boosted (can be executed directly) at arch_copy_kprobe() function. If the target instruction can be boosted, it clears "boostable" flag. If not, it sets "boostable" flag -1. This is disabled status. In resume_execution() function, If "boostable" flag is cleared, kprobe-booster measures the size of the target instruction and sets "boostable" flag 1. In kprobe_handler(), kprobe checks the "boostable" flag. If the flag is 1, it resets current kprobe and executes instruction buffer directly instead of single stepping. When unregistering a boosted kprobe, it calls synchronize_sched() after "int3" is removed. So we can ensure followings after the synchronize_sched() called. - interrupt handlers are finished on all CPUs. - instruction buffer is not executed on all CPUs. And we can release the boosted kprobe safely. And also, on preemptible kernel, the booster is not enabled where the kernel preemption is enabled. So, there are no preempted threads on the instruction buffer. The description of kretprobe-booster: ==================================== In the normal operation, kretprobe make a target function return to trampoline code. And a kprobe (called trampoline_probe) have been inserted at the trampoline code. When the kernel hits this kprobe, it calls kretprobe's handler and it returns to original return address. Kretprobe-booster patch removes the trampoline_probe. It allows the trampoline code to call kretprobe's handler directly instead of invoking kprobe. And tranpoline code returns to original return address. This new trampoline code stores and restores registers, so the kretprobe handler is still able to access those registers. Current kprobe has about 1.3 usec/probe(*) overhead, and kprobe-booster patch reduces it to 0.6 usec/probe(*). Also current kretprobe has about 2.0 usec/probe(*) overhead. Kprobe-booster patch reduces it to 1.3 usec/probe(*), and the combination of both kprobe-booster patch and kretprobe-booster patch reduces it to 0.9 usec/probe(*). I expect the combination of both patches can reduce half of a probing overhead. Performance numbers strongly depend on the processor model. Andrew Morton wrote: > These preempt tricks look rather nasty. Can you please describe what the > problem is, precisely? And how this code avoids it? Perhaps we can find > something cleaner. The problem is how to remove the copied instructions of the kprobe *safely* on the preemptable kernel (CONFIG_PREEMPT=y). Kprobes basically executes the following actions; (1)int3 (2)preempt_disable() (3)kprobe_prehandler() (4)copied instructioin(single step) (5)kprobe_posthandler() (6)preempt_enable() (7)return to the original code During the execution of copied instruction, preemption is disabled (from step (2) to (6)). When unregistering the probes, Kprobe waits for RCU quiescent state by using synchronize_sched() after removing int3 instruction. Thus we can ensure the copied instruction is not executed. On the other hand, kprobe-booster executes the following actions; (1)int3 (2)preempt_disable() (3)kprobe_prehandler() (4)preempt_enable() <-- this one is added by my patch (5)copied instruction(direct execution) (6)jmp back to the original code The problem is that we have no way to prevent preemption on step (5) or (6). We cannot call preempt_disable() after step (6), because there are no rooms to do that. Thus, some other processes may be preempted at step(5) or (6) on preemptable kernel. And I couldn't find the easy way to ensure that other processes' stack do *not* have the address of them. (I thought some way to do that, but those are very costly.) So currently, I simply boost the kprobe only when the probe point is already preemption disabled. > Also, the patch adds a preempt_enable() but I don't see a corresponding > preempt_disable(). Am I missing something? It is corresponding to the preempt_disable() in the top of kprobe_handler(). I copied the code of kprobe_handler() here: static int __kprobes kprobe_handler(struct pt_regs *regs) { struct kprobe *p; int ret = 0; kprobe_opcode_t *addr = NULL; unsigned long *lp; struct kprobe_ctlblk *kcb; /* * We don't want to be preempted for the entire * duration of kprobe processing */ preempt_disable(); <-- HERE kcb = get_kprobe_ctlblk(); Signed-off-by: Masami Hiramatsu Cc: Prasanna S Panchamukhi Cc: Ananth N Mavinakayanahalli Cc: Anil S Keshavamurthy Cc: David S. Miller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/kernel/kprobes.c | 92 +++++++++++++++++++++++++++++++++++++++++++++- include/asm-i386/kprobes.h | 6 +++ 2 files changed, 96 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index b40614f5afe2..137bf612141b 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c @@ -41,6 +41,49 @@ void jprobe_return_end(void); DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); +/* insert a jmp code */ +static inline void set_jmp_op(void *from, void *to) +{ + struct __arch_jmp_op { + char op; + long raddr; + } __attribute__((packed)) *jop; + jop = (struct __arch_jmp_op *)from; + jop->raddr = (long)(to) - ((long)(from) + 5); + jop->op = RELATIVEJUMP_INSTRUCTION; +} + +/* + * returns non-zero if opcodes can be boosted. + */ +static inline int can_boost(kprobe_opcode_t opcode) +{ + switch (opcode & 0xf0 ) { + case 0x70: + return 0; /* can't boost conditional jump */ + case 0x90: + /* can't boost call and pushf */ + return opcode != 0x9a && opcode != 0x9c; + case 0xc0: + /* can't boost undefined opcodes and soft-interruptions */ + return (0xc1 < opcode && opcode < 0xc6) || + (0xc7 < opcode && opcode < 0xcc) || opcode == 0xcf; + case 0xd0: + /* can boost AA* and XLAT */ + return (opcode == 0xd4 || opcode == 0xd5 || opcode == 0xd7); + case 0xe0: + /* can boost in/out and (may be) jmps */ + return (0xe3 < opcode && opcode != 0xe8); + case 0xf0: + /* clear and set flags can be boost */ + return (opcode == 0xf5 || (0xf7 < opcode && opcode < 0xfe)); + default: + /* currently, can't boost 2 bytes opcodes */ + return opcode != 0x0f; + } +} + + /* * returns non-zero if opcode modifies the interrupt flag. */ @@ -65,6 +108,11 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); p->opcode = *p->addr; + if (can_boost(p->opcode)) { + p->ainsn.boostable = 0; + } else { + p->ainsn.boostable = -1; + } return 0; } @@ -158,6 +206,9 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) kprobe_opcode_t *addr = NULL; unsigned long *lp; struct kprobe_ctlblk *kcb; +#ifdef CONFIG_PREEMPT + unsigned pre_preempt_count = preempt_count(); +#endif /* CONFIG_PREEMPT */ /* * We don't want to be preempted for the entire @@ -252,6 +303,21 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) /* handler has already set things up, so skip ss setup */ return 1; + if (p->ainsn.boostable == 1 && +#ifdef CONFIG_PREEMPT + !(pre_preempt_count) && /* + * This enables booster when the direct + * execution path aren't preempted. + */ +#endif /* CONFIG_PREEMPT */ + !p->post_handler && !p->break_handler ) { + /* Boost up -- we can execute copied instructions directly */ + reset_current_kprobe(); + regs->eip = (unsigned long)p->ainsn.insn; + preempt_enable_no_resched(); + return 1; + } + ss_probe: prepare_singlestep(p, regs); kcb->kprobe_status = KPROBE_HIT_SS; @@ -357,6 +423,8 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) * 2) If the single-stepped instruction was a call, the return address * that is atop the stack is the address following the copied instruction. * We need to make it the address following the original instruction. + * + * This function also checks instruction size for preparing direct execution. */ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) @@ -377,6 +445,7 @@ static void __kprobes resume_execution(struct kprobe *p, case 0xca: case 0xea: /* jmp absolute -- eip is correct */ /* eip is already adjusted, no more changes required */ + p->ainsn.boostable = 1; goto no_change; case 0xe8: /* call relative - Fix return addr */ *tos = orig_eip + (*tos - copy_eip); @@ -384,18 +453,37 @@ static void __kprobes resume_execution(struct kprobe *p, case 0xff: if ((p->ainsn.insn[1] & 0x30) == 0x10) { /* call absolute, indirect */ - /* Fix return addr; eip is correct. */ + /* + * Fix return addr; eip is correct. + * But this is not boostable + */ *tos = orig_eip + (*tos - copy_eip); goto no_change; } else if (((p->ainsn.insn[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */ ((p->ainsn.insn[1] & 0x31) == 0x21)) { /* jmp far, absolute indirect */ - /* eip is correct. */ + /* eip is correct. And this is boostable */ + p->ainsn.boostable = 1; goto no_change; } default: break; } + if (p->ainsn.boostable == 0) { + if ((regs->eip > copy_eip) && + (regs->eip - copy_eip) + 5 < MAX_INSN_SIZE) { + /* + * These instructions can be executed directly if it + * jumps back to correct address. + */ + set_jmp_op((void *)regs->eip, + (void *)orig_eip + (regs->eip - copy_eip)); + p->ainsn.boostable = 1; + } else { + p->ainsn.boostable = -1; + } + } + regs->eip = orig_eip + (regs->eip - copy_eip); no_change: diff --git a/include/asm-i386/kprobes.h b/include/asm-i386/kprobes.h index a0d2d74a7dda..57d157c5cf89 100644 --- a/include/asm-i386/kprobes.h +++ b/include/asm-i386/kprobes.h @@ -34,6 +34,7 @@ struct pt_regs; typedef u8 kprobe_opcode_t; #define BREAKPOINT_INSTRUCTION 0xcc +#define RELATIVEJUMP_INSTRUCTION 0xe9 #define MAX_INSN_SIZE 16 #define MAX_STACK_SIZE 64 #define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \ @@ -51,6 +52,11 @@ void kretprobe_trampoline(void); struct arch_specific_insn { /* copy of the original instruction */ kprobe_opcode_t *insn; + /* + * If this flag is not 0, this kprobe can be boost when its + * post_handler and break_handler is not set. + */ + int boostable; }; struct prev_kprobe { -- cgit v1.2.3 From ee8a4b7f857fe7ba243e65c8925798cf8eda5ab0 Mon Sep 17 00:00:00 2001 From: Hansjoerg Lipp Date: Sun, 26 Mar 2006 01:38:32 -0800 Subject: [PATCH] isdn4linux: Siemens Gigaset drivers - tty interface And: Tilman Schmidt This patch adds the tty interface to the gigaset module. The tty interface provides direct access to the AT command set of the Gigaset devices. Signed-off-by: Hansjoerg Lipp Signed-off-by: Tilman Schmidt Cc: Karsten Keil Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/isdn/gigaset/interface.c | 718 +++++++++++++++++++++++++++++++++++++++ include/linux/gigaset_dev.h | 32 ++ 2 files changed, 750 insertions(+) create mode 100644 drivers/isdn/gigaset/interface.c create mode 100644 include/linux/gigaset_dev.h (limited to 'include') diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c new file mode 100644 index 000000000000..3a81d9c65141 --- /dev/null +++ b/drivers/isdn/gigaset/interface.c @@ -0,0 +1,718 @@ +/* + * interface to user space for the gigaset driver + * + * Copyright (c) 2004 by Hansjoerg Lipp + * + * ===================================================================== + * 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. + * ===================================================================== + * Version: $Id: interface.c,v 1.14.4.15 2006/02/04 18:28:16 hjlipp Exp $ + * ===================================================================== + */ + +#include "gigaset.h" +#include +#include +#include + +/*** our ioctls ***/ + +static int if_lock(struct cardstate *cs, int *arg) +{ + int cmd = *arg; + + dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd); + + if (cmd > 1) + return -EINVAL; + + if (cmd < 0) { + *arg = atomic_read(&cs->mstate) == MS_LOCKED; //FIXME remove? + return 0; + } + + if (!cmd && atomic_read(&cs->mstate) == MS_LOCKED + && atomic_read(&cs->connected)) { + cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS); + cs->ops->baud_rate(cs, B115200); + cs->ops->set_line_ctrl(cs, CS8); + cs->control_state = TIOCM_DTR|TIOCM_RTS; + } + + cs->waiting = 1; + if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK, + NULL, cmd, NULL)) { + cs->waiting = 0; + return -ENOMEM; + } + + dbg(DEBUG_CMD, "scheduling IF_LOCK"); + gigaset_schedule_event(cs); + + wait_event(cs->waitqueue, !cs->waiting); + + if (cs->cmd_result >= 0) { + *arg = cs->cmd_result; + return 0; + } + + return cs->cmd_result; +} + +static int if_version(struct cardstate *cs, unsigned arg[4]) +{ + static const unsigned version[4] = GIG_VERSION; + static const unsigned compat[4] = GIG_COMPAT; + unsigned cmd = arg[0]; + + dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd); + + switch (cmd) { + case GIGVER_DRIVER: + memcpy(arg, version, sizeof version); + return 0; + case GIGVER_COMPAT: + memcpy(arg, compat, sizeof compat); + return 0; + case GIGVER_FWBASE: + cs->waiting = 1; + if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER, + NULL, 0, arg)) { + cs->waiting = 0; + return -ENOMEM; + } + + dbg(DEBUG_CMD, "scheduling IF_VER"); + gigaset_schedule_event(cs); + + wait_event(cs->waitqueue, !cs->waiting); + + if (cs->cmd_result >= 0) + return 0; + + return cs->cmd_result; + default: + return -EINVAL; + } +} + +static int if_config(struct cardstate *cs, int *arg) +{ + dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg); + + if (*arg != 1) + return -EINVAL; + + if (atomic_read(&cs->mstate) != MS_LOCKED) + return -EBUSY; + + *arg = 0; + return gigaset_enterconfigmode(cs); +} + +/*** the terminal driver ***/ +/* stolen from usbserial and some other tty drivers */ + +static int if_open(struct tty_struct *tty, struct file *filp); +static void if_close(struct tty_struct *tty, struct file *filp); +static int if_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +static int if_write_room(struct tty_struct *tty); +static int if_chars_in_buffer(struct tty_struct *tty); +static void if_throttle(struct tty_struct *tty); +static void if_unthrottle(struct tty_struct *tty); +static void if_set_termios(struct tty_struct *tty, struct termios *old); +static int if_tiocmget(struct tty_struct *tty, struct file *file); +static int if_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); +static int if_write(struct tty_struct *tty, + const unsigned char *buf, int count); + +static struct tty_operations if_ops = { + .open = if_open, + .close = if_close, + .ioctl = if_ioctl, + .write = if_write, + .write_room = if_write_room, + .chars_in_buffer = if_chars_in_buffer, + .set_termios = if_set_termios, + .throttle = if_throttle, + .unthrottle = if_unthrottle, +#if 0 + .break_ctl = serial_break, +#endif + .tiocmget = if_tiocmget, + .tiocmset = if_tiocmset, +}; + +static int if_open(struct tty_struct *tty, struct file *filp) +{ + struct cardstate *cs; + unsigned long flags; + + dbg(DEBUG_IF, "%d+%d: %s()", tty->driver->minor_start, tty->index, + __FUNCTION__); + + tty->driver_data = NULL; + + cs = gigaset_get_cs_by_tty(tty); + if (!cs) + return -ENODEV; + + if (down_interruptible(&cs->sem)) + return -ERESTARTSYS; // FIXME -EINTR? + tty->driver_data = cs; + + ++cs->open_count; + + if (cs->open_count == 1) { + spin_lock_irqsave(&cs->lock, flags); + cs->tty = tty; + spin_unlock_irqrestore(&cs->lock, flags); + tty->low_latency = 1; //FIXME test + //FIXME + } + + up(&cs->sem); + return 0; +} + +static void if_close(struct tty_struct *tty, struct file *filp) +{ + struct cardstate *cs; + unsigned long flags; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return; + } + + dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + + down(&cs->sem); + + if (!cs->open_count) + warn("%s: device not opened", __FUNCTION__); + else { + if (!--cs->open_count) { + spin_lock_irqsave(&cs->lock, flags); + cs->tty = NULL; + spin_unlock_irqrestore(&cs->lock, flags); + //FIXME + } + } + + up(&cs->sem); +} + +static int if_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct cardstate *cs; + int retval = -ENODEV; + int int_arg; + unsigned char buf[6]; + unsigned version[4]; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return -ENODEV; + } + + dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __FUNCTION__, cmd); + + if (down_interruptible(&cs->sem)) + return -ERESTARTSYS; // FIXME -EINTR? + + if (!cs->open_count) + warn("%s: device not opened", __FUNCTION__); + else { + retval = 0; + switch (cmd) { + case GIGASET_REDIR: + retval = get_user(int_arg, (int __user *) arg); + if (retval >= 0) + retval = if_lock(cs, &int_arg); + if (retval >= 0) + retval = put_user(int_arg, (int __user *) arg); + break; + case GIGASET_CONFIG: + retval = get_user(int_arg, (int __user *) arg); + if (retval >= 0) + retval = if_config(cs, &int_arg); + if (retval >= 0) + retval = put_user(int_arg, (int __user *) arg); + break; + case GIGASET_BRKCHARS: + //FIXME test if MS_LOCKED + gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS", + 6, (const unsigned char *) arg, 1); + if (!atomic_read(&cs->connected)) { + dbg(DEBUG_ANY, "can't communicate with unplugged device"); + retval = -ENODEV; + break; + } + retval = copy_from_user(&buf, + (const unsigned char __user *) arg, 6) + ? -EFAULT : 0; + if (retval >= 0) + retval = cs->ops->brkchars(cs, buf); + break; + case GIGASET_VERSION: + retval = copy_from_user(version, (unsigned __user *) arg, + sizeof version) ? -EFAULT : 0; + if (retval >= 0) + retval = if_version(cs, version); + if (retval >= 0) + retval = copy_to_user((unsigned __user *) arg, version, + sizeof version) + ? -EFAULT : 0; + break; + default: + dbg(DEBUG_ANY, "%s: arg not supported - 0x%04x", + __FUNCTION__, cmd); + retval = -ENOIOCTLCMD; + } + } + + up(&cs->sem); + + return retval; +} + +static int if_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct cardstate *cs; + int retval; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return -ENODEV; + } + + dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + + if (down_interruptible(&cs->sem)) + return -ERESTARTSYS; // FIXME -EINTR? + + // FIXME read from device? + retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR); + + up(&cs->sem); + + return retval; +} + +static int if_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct cardstate *cs; + int retval; + unsigned mc; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return -ENODEV; + } + + dbg(DEBUG_IF, + "%u: %s(0x%x, 0x%x)", cs->minor_index, __FUNCTION__, set, clear); + + if (down_interruptible(&cs->sem)) + return -ERESTARTSYS; // FIXME -EINTR? + + if (!atomic_read(&cs->connected)) { + dbg(DEBUG_ANY, "can't communicate with unplugged device"); + retval = -ENODEV; + } else { + mc = (cs->control_state | set) & ~clear & (TIOCM_RTS|TIOCM_DTR); + retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc); + cs->control_state = mc; + } + + up(&cs->sem); + + return retval; +} + +static int if_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + struct cardstate *cs; + int retval = -ENODEV; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return -ENODEV; + } + + dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + + if (down_interruptible(&cs->sem)) + return -ERESTARTSYS; // FIXME -EINTR? + + if (!cs->open_count) + warn("%s: device not opened", __FUNCTION__); + else if (atomic_read(&cs->mstate) != MS_LOCKED) { + warn("can't write to unlocked device"); + retval = -EBUSY; + } else if (!atomic_read(&cs->connected)) { + dbg(DEBUG_ANY, "can't write to unplugged device"); + retval = -EBUSY; //FIXME + } else { + retval = cs->ops->write_cmd(cs, buf, count, + &cs->if_wake_tasklet); + } + + up(&cs->sem); + + return retval; +} + +static int if_write_room(struct tty_struct *tty) +{ + struct cardstate *cs; + int retval = -ENODEV; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return -ENODEV; + } + + dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + + if (down_interruptible(&cs->sem)) + return -ERESTARTSYS; // FIXME -EINTR? + + if (!cs->open_count) + warn("%s: device not opened", __FUNCTION__); + else if (atomic_read(&cs->mstate) != MS_LOCKED) { + warn("can't write to unlocked device"); + retval = -EBUSY; //FIXME + } else if (!atomic_read(&cs->connected)) { + dbg(DEBUG_ANY, "can't write to unplugged device"); + retval = -EBUSY; //FIXME + } else + retval = cs->ops->write_room(cs); + + up(&cs->sem); + + return retval; +} + +static int if_chars_in_buffer(struct tty_struct *tty) +{ + struct cardstate *cs; + int retval = -ENODEV; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return -ENODEV; + } + + dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + + if (down_interruptible(&cs->sem)) + return -ERESTARTSYS; // FIXME -EINTR? + + if (!cs->open_count) + warn("%s: device not opened", __FUNCTION__); + else if (atomic_read(&cs->mstate) != MS_LOCKED) { + warn("can't write to unlocked device"); + retval = -EBUSY; + } else if (!atomic_read(&cs->connected)) { + dbg(DEBUG_ANY, "can't write to unplugged device"); + retval = -EBUSY; //FIXME + } else + retval = cs->ops->chars_in_buffer(cs); + + up(&cs->sem); + + return retval; +} + +static void if_throttle(struct tty_struct *tty) +{ + struct cardstate *cs; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return; + } + + dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + + down(&cs->sem); + + if (!cs->open_count) + warn("%s: device not opened", __FUNCTION__); + else { + //FIXME + } + + up(&cs->sem); +} + +static void if_unthrottle(struct tty_struct *tty) +{ + struct cardstate *cs; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return; + } + + dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + + down(&cs->sem); + + if (!cs->open_count) + warn("%s: device not opened", __FUNCTION__); + else { + //FIXME + } + + up(&cs->sem); +} + +static void if_set_termios(struct tty_struct *tty, struct termios *old) +{ + struct cardstate *cs; + unsigned int iflag; + unsigned int cflag; + unsigned int old_cflag; + unsigned int control_state, new_state; + + cs = (struct cardstate *) tty->driver_data; + if (!cs) { + err("cs==NULL in %s", __FUNCTION__); + return; + } + + dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + + down(&cs->sem); + + if (!cs->open_count) { + warn("%s: device not opened", __FUNCTION__); + goto out; + } + + if (!atomic_read(&cs->connected)) { + dbg(DEBUG_ANY, "can't communicate with unplugged device"); + goto out; + } + + // stolen from mct_u232.c + iflag = tty->termios->c_iflag; + cflag = tty->termios->c_cflag; + old_cflag = old ? old->c_cflag : cflag; //FIXME? + dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x", cs->minor_index, + iflag, cflag, old_cflag); + + /* get a local copy of the current port settings */ + control_state = cs->control_state; + + /* + * Update baud rate. + * Do not attempt to cache old rates and skip settings, + * disconnects screw such tricks up completely. + * Premature optimization is the root of all evil. + */ + + /* reassert DTR and (maybe) RTS on transition from B0 */ + if ((old_cflag & CBAUD) == B0) { + new_state = control_state | TIOCM_DTR; + /* don't set RTS if using hardware flow control */ + if (!(old_cflag & CRTSCTS)) + new_state |= TIOCM_RTS; + dbg(DEBUG_IF, "%u: from B0 - set DTR%s", cs->minor_index, + (new_state & TIOCM_RTS) ? " only" : "/RTS"); + cs->ops->set_modem_ctrl(cs, control_state, new_state); + control_state = new_state; + } + + cs->ops->baud_rate(cs, cflag & CBAUD); + + if ((cflag & CBAUD) == B0) { + /* Drop RTS and DTR */ + dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index); + new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS); + cs->ops->set_modem_ctrl(cs, control_state, new_state); + control_state = new_state; + } + + /* + * Update line control register (LCR) + */ + + cs->ops->set_line_ctrl(cs, cflag); + +#if 0 + //FIXME this hangs M101 [ts 2005-03-09] + //FIXME do we need this? + /* + * Set flow control: well, I do not really now how to handle DTR/RTS. + * Just do what we have seen with SniffUSB on Win98. + */ + /* Drop DTR/RTS if no flow control otherwise assert */ + dbg(DEBUG_IF, "%u: control_state %x", cs->minor_index, control_state); + new_state = control_state; + if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS)) + new_state |= TIOCM_DTR | TIOCM_RTS; + else + new_state &= ~(TIOCM_DTR | TIOCM_RTS); + if (new_state != control_state) { + dbg(DEBUG_IF, "%u: new_state %x", cs->minor_index, new_state); + gigaset_set_modem_ctrl(cs, control_state, new_state); // FIXME: mct_u232.c sets the old state here. is this a bug? + control_state = new_state; + } +#endif + + /* save off the modified port settings */ + cs->control_state = control_state; + +out: + up(&cs->sem); +} + + +/* wakeup tasklet for the write operation */ +static void if_wake(unsigned long data) +{ + struct cardstate *cs = (struct cardstate *) data; + struct tty_struct *tty; + + tty = cs->tty; + if (!tty) + return; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) { + dbg(DEBUG_IF, "write wakeup call"); + tty->ldisc.write_wakeup(tty); + } + + wake_up_interruptible(&tty->write_wait); +} + +/*** interface to common ***/ + +void gigaset_if_init(struct cardstate *cs) +{ + struct gigaset_driver *drv; + + drv = cs->driver; + if (!drv->have_tty) + return; + + tasklet_init(&cs->if_wake_tasklet, &if_wake, (unsigned long) cs); + tty_register_device(drv->tty, cs->minor_index, NULL); +} + +void gigaset_if_free(struct cardstate *cs) +{ + struct gigaset_driver *drv; + + drv = cs->driver; + if (!drv->have_tty) + return; + + tasklet_disable(&cs->if_wake_tasklet); + tasklet_kill(&cs->if_wake_tasklet); + tty_unregister_device(drv->tty, cs->minor_index); +} + +void gigaset_if_receive(struct cardstate *cs, + unsigned char *buffer, size_t len) +{ + unsigned long flags; + struct tty_struct *tty; + + spin_lock_irqsave(&cs->lock, flags); + if ((tty = cs->tty) == NULL) + dbg(DEBUG_ANY, "receive on closed device"); + else { + tty_buffer_request_room(tty, len); + tty_insert_flip_string(tty, buffer, len); + tty_flip_buffer_push(tty); + } + spin_unlock_irqrestore(&cs->lock, flags); +} +EXPORT_SYMBOL_GPL(gigaset_if_receive); + +/* gigaset_if_initdriver + * Initialize tty interface. + * parameters: + * drv Driver + * procname Name of the driver (e.g. for /proc/tty/drivers) + * devname Name of the device files (prefix without minor number) + * devfsname Devfs name of the device files without %d + */ +void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname, + const char *devname, const char *devfsname) +{ + unsigned minors = drv->minors; + int ret; + struct tty_driver *tty; + + drv->have_tty = 0; + + if ((drv->tty = alloc_tty_driver(minors)) == NULL) + goto enomem; + tty = drv->tty; + + tty->magic = TTY_DRIVER_MAGIC, + tty->major = GIG_MAJOR, + tty->type = TTY_DRIVER_TYPE_SERIAL, + tty->subtype = SERIAL_TYPE_NORMAL, + tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + + tty->driver_name = procname; + tty->name = devname; + tty->minor_start = drv->minor; + tty->num = drv->minors; + + tty->owner = THIS_MODULE; + tty->devfs_name = devfsname; + + tty->init_termios = tty_std_termios; //FIXME + tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //FIXME + tty_set_operations(tty, &if_ops); + + ret = tty_register_driver(tty); + if (ret < 0) { + warn("failed to register tty driver (error %d)", ret); + goto error; + } + dbg(DEBUG_IF, "tty driver initialized"); + drv->have_tty = 1; + return; + +enomem: + warn("could not allocate tty structures"); +error: + if (drv->tty) + put_tty_driver(drv->tty); +} + +void gigaset_if_freedriver(struct gigaset_driver *drv) +{ + if (!drv->have_tty) + return; + + drv->have_tty = 0; + tty_unregister_driver(drv->tty); + put_tty_driver(drv->tty); +} diff --git a/include/linux/gigaset_dev.h b/include/linux/gigaset_dev.h new file mode 100644 index 000000000000..70ad09c8ad1e --- /dev/null +++ b/include/linux/gigaset_dev.h @@ -0,0 +1,32 @@ +/* + * interface to user space for the gigaset driver + * + * Copyright (c) 2004 by Hansjoerg Lipp + * + * ===================================================================== + * 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. + * ===================================================================== + * Version: $Id: gigaset_dev.h,v 1.4.4.4 2005/11/21 22:28:09 hjlipp Exp $ + * ===================================================================== + */ + +#ifndef GIGASET_INTERFACE_H +#define GIGASET_INTERFACE_H + +#include + +#define GIGASET_IOCTL 0x47 + +#define GIGVER_DRIVER 0 +#define GIGVER_COMPAT 1 +#define GIGVER_FWBASE 2 + +#define GIGASET_REDIR _IOWR (GIGASET_IOCTL, 0, int) +#define GIGASET_CONFIG _IOWR (GIGASET_IOCTL, 1, int) +#define GIGASET_BRKCHARS _IOW (GIGASET_IOCTL, 2, unsigned char[6]) //FIXME [6] okay? +#define GIGASET_VERSION _IOWR (GIGASET_IOCTL, 3, unsigned[4]) + +#endif -- cgit v1.2.3 From 0b28002fdf2d5b6ce3135a544c04940a16c5b0ba Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:38:58 -0800 Subject: [PATCH] more s/fucn/func/ typo fixes s/fucntion/function/ typo fixes Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 2 +- arch/m68k/bvme6000/config.c | 2 +- arch/s390/crypto/crypt_s390_query.c | 2 +- drivers/acpi/processor_core.c | 2 +- drivers/net/sis900.c | 4 ++-- include/linux/gameport.h | 4 ++-- include/linux/serio.h | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index 6dc9d9f622ca..6feef9e82b63 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -2836,7 +2836,7 @@ struct _snd_pcm_runtime { Note that this callback became non-atomic since the recent version. - You can use schedule-related fucntions safely in this callback now. + You can use schedule-related functions safely in this callback now. diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c index 3ffc84f9c291..c90cb5fcc8ef 100644 --- a/arch/m68k/bvme6000/config.c +++ b/arch/m68k/bvme6000/config.c @@ -142,7 +142,7 @@ void __init config_bvme6000(void) /* Now do the PIT configuration */ pit->pgcr = 0x00; /* Unidirectional 8 bit, no handshake for now */ - pit->psrr = 0x18; /* PIACK and PIRQ fucntions enabled */ + pit->psrr = 0x18; /* PIACK and PIRQ functions enabled */ pit->pacr = 0x00; /* Sub Mode 00, H2 i/p, no DMA */ pit->padr = 0x00; /* Just to be tidy! */ pit->paddr = 0x00; /* All inputs for now (safest) */ diff --git a/arch/s390/crypto/crypt_s390_query.c b/arch/s390/crypto/crypt_s390_query.c index def02bdc44a4..54fb11d7fadd 100644 --- a/arch/s390/crypto/crypt_s390_query.c +++ b/arch/s390/crypto/crypt_s390_query.c @@ -55,7 +55,7 @@ static void query_available_functions(void) printk(KERN_INFO "KMC_AES_256: %d\n", crypt_s390_func_available(KMC_AES_256_ENCRYPT)); - /* query available KIMD fucntions */ + /* query available KIMD functions */ printk(KERN_INFO "KIMD_QUERY: %d\n", crypt_s390_func_available(KIMD_QUERY)); printk(KERN_INFO "KIMD_SHA_1: %d\n", diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 99a3a28594da..713b763884a9 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -246,7 +246,7 @@ static int acpi_processor_errata(struct acpi_processor *pr) } /* -------------------------------------------------------------------------- - Common ACPI processor fucntions + Common ACPI processor functions -------------------------------------------------------------------------- */ /* diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index 253440a98022..8429ceb01389 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -1693,7 +1693,7 @@ static irqreturn_t sis900_interrupt(int irq, void *dev_instance, struct pt_regs * * Process receive interrupt events, * put buffer to higher layer and refill buffer pool - * Note: This fucntion is called by interrupt handler, + * Note: This function is called by interrupt handler, * don't do "too much" work here */ @@ -1840,7 +1840,7 @@ static int sis900_rx(struct net_device *net_dev) * * Check for error condition and free socket buffer etc * schedule for more transmission as needed - * Note: This fucntion is called by interrupt handler, + * Note: This function is called by interrupt handler, * don't do "too much" work here */ diff --git a/include/linux/gameport.h b/include/linux/gameport.h index 2401dea2b867..9c8e6da2393b 100644 --- a/include/linux/gameport.h +++ b/include/linux/gameport.h @@ -119,7 +119,7 @@ static inline void gameport_set_name(struct gameport *gameport, const char *name } /* - * Use the following fucntions to manipulate gameport's per-port + * Use the following functions to manipulate gameport's per-port * driver-specific data. */ static inline void *gameport_get_drvdata(struct gameport *gameport) @@ -133,7 +133,7 @@ static inline void gameport_set_drvdata(struct gameport *gameport, void *data) } /* - * Use the following fucntions to pin gameport's driver in process context + * Use the following functions to pin gameport's driver in process context */ static inline int gameport_pin_driver(struct gameport *gameport) { diff --git a/include/linux/serio.h b/include/linux/serio.h index aa4d6493a034..690aabca8ed0 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -119,7 +119,7 @@ static inline void serio_cleanup(struct serio *serio) } /* - * Use the following fucntions to manipulate serio's per-port + * Use the following functions to manipulate serio's per-port * driver-specific data. */ static inline void *serio_get_drvdata(struct serio *serio) @@ -133,7 +133,7 @@ static inline void serio_set_drvdata(struct serio *serio, void *data) } /* - * Use the following fucntions to protect critical sections in + * Use the following functions to protect critical sections in * driver code from port's interrupt handler */ static inline void serio_pause_rx(struct serio *serio) @@ -147,7 +147,7 @@ static inline void serio_continue_rx(struct serio *serio) } /* - * Use the following fucntions to pin serio's driver in process context + * Use the following functions to pin serio's driver in process context */ static inline int serio_pin_driver(struct serio *serio) { -- cgit v1.2.3 From 93635133663ea3155e74a0e3645b64754a046007 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:38:59 -0800 Subject: [PATCH] arm: fix undefined reference to generic_fls This patch defines constant_fls() instead of removed generic_fls(). Signed-off-by: Akinobu Mita Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-arm/bitops.h | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-arm/bitops.h b/include/asm-arm/bitops.h index d02de721ecc1..eaecd553e856 100644 --- a/include/asm-arm/bitops.h +++ b/include/asm-arm/bitops.h @@ -344,13 +344,42 @@ static inline unsigned long __ffs(unsigned long word) #else +static inline int constant_fls(int x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xffff0000u)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xff000000u)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf0000000u)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc0000000u)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000u)) { + x <<= 1; + r -= 1; + } + return r; +} + /* * On ARMv5 and above those functions can be implemented around * the clz instruction for much better code efficiency. */ #define fls(x) \ - ( __builtin_constant_p(x) ? generic_fls(x) : \ + ( __builtin_constant_p(x) ? constant_fls(x) : \ ({ int __r; asm("clz\t%0, %1" : "=r"(__r) : "r"(x) : "cc"); 32-__r; }) ) #define fls64(x) generic_fls64(x) #define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); }) -- cgit v1.2.3 From 4b417d0c7cb6af4afb7bad6463f7f3227d8a5074 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:01 -0800 Subject: [PATCH] bitops: alpha: use config options instead of __alpha_fix__ and __alpha_cix__ Use config options instead of gcc builtin definition to tell the use of instruction set extensions (CIX and FIX). This is introduced to tell the kbuild system the use of opmized hweight*() routines on alpha architecture. Signed-off-by: Akinobu Mita Cc: Richard Henderson Cc: Ivan Kokshaysky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/lib/ev6-memchr.S | 2 +- arch/alpha/lib/fpreg.c | 8 ++++---- include/asm-alpha/bitops.h | 10 +++++----- include/asm-alpha/fpu.h | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/arch/alpha/lib/ev6-memchr.S b/arch/alpha/lib/ev6-memchr.S index a8e843dbcc23..1a5f71b9d8b1 100644 --- a/arch/alpha/lib/ev6-memchr.S +++ b/arch/alpha/lib/ev6-memchr.S @@ -84,7 +84,7 @@ $last_quad: beq $2, $not_found # U : U L U L $found_it: -#if defined(__alpha_fix__) && defined(__alpha_cix__) +#ifdef CONFIG_ALPHA_EV67 /* * Since we are guaranteed to have set one of the bits, we don't * have to worry about coming back with a 0x40 out of cttz... diff --git a/arch/alpha/lib/fpreg.c b/arch/alpha/lib/fpreg.c index 97c4d9d7a4d5..05017ba34c3c 100644 --- a/arch/alpha/lib/fpreg.c +++ b/arch/alpha/lib/fpreg.c @@ -4,7 +4,7 @@ * (C) Copyright 1998 Linus Torvalds */ -#if defined(__alpha_cix__) || defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67) #define STT(reg,val) asm volatile ("ftoit $f"#reg",%0" : "=r"(val)); #else #define STT(reg,val) asm volatile ("stt $f"#reg",%0" : "=m"(val)); @@ -53,7 +53,7 @@ alpha_read_fp_reg (unsigned long reg) return val; } -#if defined(__alpha_cix__) || defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67) #define LDT(reg,val) asm volatile ("itoft %0,$f"#reg : : "r"(val)); #else #define LDT(reg,val) asm volatile ("ldt $f"#reg",%0" : : "m"(val)); @@ -98,7 +98,7 @@ alpha_write_fp_reg (unsigned long reg, unsigned long val) } } -#if defined(__alpha_cix__) || defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67) #define STS(reg,val) asm volatile ("ftois $f"#reg",%0" : "=r"(val)); #else #define STS(reg,val) asm volatile ("sts $f"#reg",%0" : "=m"(val)); @@ -147,7 +147,7 @@ alpha_read_fp_reg_s (unsigned long reg) return val; } -#if defined(__alpha_cix__) || defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67) #define LDS(reg,val) asm volatile ("itofs %0,$f"#reg : : "r"(val)); #else #define LDS(reg,val) asm volatile ("lds $f"#reg",%0" : : "m"(val)); diff --git a/include/asm-alpha/bitops.h b/include/asm-alpha/bitops.h index 302201f1a097..e3e602fe5060 100644 --- a/include/asm-alpha/bitops.h +++ b/include/asm-alpha/bitops.h @@ -261,7 +261,7 @@ static inline unsigned long ffz_b(unsigned long x) static inline unsigned long ffz(unsigned long word) { -#if defined(__alpha_cix__) && defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67) /* Whee. EV67 can calculate it directly. */ return __kernel_cttz(~word); #else @@ -281,7 +281,7 @@ static inline unsigned long ffz(unsigned long word) */ static inline unsigned long __ffs(unsigned long word) { -#if defined(__alpha_cix__) && defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67) /* Whee. EV67 can calculate it directly. */ return __kernel_cttz(word); #else @@ -313,7 +313,7 @@ static inline int ffs(int word) /* * fls: find last bit set. */ -#if defined(__alpha_cix__) && defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67) static inline int fls(int word) { return 64 - __kernel_ctlz(word & 0xffffffff); @@ -326,7 +326,7 @@ static inline int fls(int word) /* Compute powers of two for the given integer. */ static inline long floor_log2(unsigned long word) { -#if defined(__alpha_cix__) && defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67) return 63 - __kernel_ctlz(word); #else long bit; @@ -347,7 +347,7 @@ static inline long ceil_log2(unsigned long word) * of bits set) of a N-bit word */ -#if defined(__alpha_cix__) && defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67) /* Whee. EV67 can calculate it directly. */ static inline unsigned long hweight64(unsigned long w) { diff --git a/include/asm-alpha/fpu.h b/include/asm-alpha/fpu.h index c203fc2fa5cd..ecb17a72acc3 100644 --- a/include/asm-alpha/fpu.h +++ b/include/asm-alpha/fpu.h @@ -130,7 +130,7 @@ rdfpcr(void) { unsigned long tmp, ret; -#if defined(__alpha_cix__) || defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67) __asm__ __volatile__ ( "ftoit $f0,%0\n\t" "mf_fpcr $f0\n\t" @@ -154,7 +154,7 @@ wrfpcr(unsigned long val) { unsigned long tmp; -#if defined(__alpha_cix__) || defined(__alpha_fix__) +#if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67) __asm__ __volatile__ ( "ftoit $f0,%0\n\t" "itoft %1,$f0\n\t" -- cgit v1.2.3 From 3156054b02e263387d6cd27fd1b05eba2bc25459 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:04 -0800 Subject: [PATCH] bitops: parisc: add ()-pair in __ffz() macro Noticed by Michael Tokarev add missing ()-pair in __ffz() macro for parisc Signed-off-by: Akinobu Mita Cc: Kyle McMartin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-parisc/bitops.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-parisc/bitops.h b/include/asm-parisc/bitops.h index 15d8c2b51584..ca6119af20af 100644 --- a/include/asm-parisc/bitops.h +++ b/include/asm-parisc/bitops.h @@ -220,7 +220,7 @@ static __inline__ unsigned long __ffs(unsigned long x) } /* Undefined if no bit is zero. */ -#define ffz(x) __ffs(~x) +#define ffz(x) __ffs(~(x)) /* * ffs: find first bit set. returns 1 to BITS_PER_LONG or 0 (if none set) -- cgit v1.2.3 From 72b61a3cfd80d1321eb898be8ceae2064f0fbea1 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:04 -0800 Subject: [PATCH] bitops: cris: remove unnecessary local_irq_restore() remove unnecessary local_irq_restore() after cris_atomic_restore() in test_and_set_bit(). Signed-off-by: Akinobu Mita Cc: Mikael Starvik Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-cris/bitops.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/asm-cris/bitops.h b/include/asm-cris/bitops.h index b7fef1572dc0..f8a674769314 100644 --- a/include/asm-cris/bitops.h +++ b/include/asm-cris/bitops.h @@ -101,7 +101,6 @@ static inline int test_and_set_bit(int nr, volatile unsigned long *addr) retval = (mask & *adr) != 0; *adr |= mask; cris_atomic_restore(addr, flags); - local_irq_restore(flags); return retval; } -- cgit v1.2.3 From 67b0ad574b5ee90f8ea58196ff8a7f3780b75365 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:05 -0800 Subject: [PATCH] bitops: use non atomic operations for minix_*_bit() and ext2_*_bit() Bitmap functions for the minix filesystem and the ext2 filesystem except ext2_set_bit_atomic() and ext2_clear_bit_atomic() do not require the atomic guarantees. But these are defined by using atomic bit operations on several architectures. (cris, frv, h8300, ia64, m32r, m68k, m68knommu, mips, s390, sh, sh64, sparc, sparc64, v850, and xtensa) This patch switches to non atomic bit operation. Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-cris/bitops.h | 8 ++++---- include/asm-frv/bitops.h | 14 +++++++------- include/asm-h8300/bitops.h | 6 +++--- include/asm-ia64/bitops.h | 10 +++++----- include/asm-m32r/bitops.h | 2 +- include/asm-m68k/bitops.h | 10 +++++----- include/asm-m68knommu/bitops.h | 6 +++--- include/asm-mips/bitops.h | 6 +++--- include/asm-s390/bitops.h | 10 +++++----- include/asm-sh/bitops.h | 16 +++++----------- include/asm-sh64/bitops.h | 16 +++++----------- include/asm-sparc/bitops.h | 6 +++--- include/asm-sparc64/bitops.h | 6 +++--- include/asm-v850/bitops.h | 10 +++++----- include/asm-xtensa/bitops.h | 6 +++--- 15 files changed, 60 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/include/asm-cris/bitops.h b/include/asm-cris/bitops.h index f8a674769314..b1fca1fd8a5f 100644 --- a/include/asm-cris/bitops.h +++ b/include/asm-cris/bitops.h @@ -352,17 +352,17 @@ found_middle: #define find_first_bit(addr, size) \ find_next_bit((addr), (size), 0) -#define ext2_set_bit test_and_set_bit +#define ext2_set_bit __test_and_set_bit #define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) -#define ext2_clear_bit test_and_clear_bit +#define ext2_clear_bit __test_and_clear_bit #define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) #define ext2_test_bit test_bit #define ext2_find_first_zero_bit find_first_zero_bit #define ext2_find_next_zero_bit find_next_zero_bit /* Bitmap functions for the minix filesystem. */ -#define minix_set_bit(nr,addr) test_and_set_bit(nr,addr) -#define minix_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) diff --git a/include/asm-frv/bitops.h b/include/asm-frv/bitops.h index f686b519878e..9c5db5c34c1b 100644 --- a/include/asm-frv/bitops.h +++ b/include/asm-frv/bitops.h @@ -259,11 +259,11 @@ static inline int sched_find_first_bit(const unsigned long *b) #define hweight16(x) generic_hweight16(x) #define hweight8(x) generic_hweight8(x) -#define ext2_set_bit(nr, addr) test_and_set_bit ((nr) ^ 0x18, (addr)) -#define ext2_clear_bit(nr, addr) test_and_clear_bit((nr) ^ 0x18, (addr)) +#define ext2_set_bit(nr, addr) __test_and_set_bit ((nr) ^ 0x18, (addr)) +#define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr) ^ 0x18, (addr)) -#define ext2_set_bit_atomic(lock,nr,addr) ext2_set_bit((nr), addr) -#define ext2_clear_bit_atomic(lock,nr,addr) ext2_clear_bit((nr), addr) +#define ext2_set_bit_atomic(lock,nr,addr) test_and_set_bit ((nr) ^ 0x18, (addr)) +#define ext2_clear_bit_atomic(lock,nr,addr) test_and_clear_bit((nr) ^ 0x18, (addr)) static inline int ext2_test_bit(int nr, const volatile void * addr) { @@ -331,9 +331,9 @@ found_middle: } /* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) ext2_set_bit(nr,addr) -#define minix_set_bit(nr,addr) ext2_set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) ext2_clear_bit(nr,addr) +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit ((nr) ^ 0x18, (addr)) +#define minix_set_bit(nr,addr) __set_bit((nr) ^ 0x18, (addr)) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit((nr) ^ 0x18, (addr)) #define minix_test_bit(nr,addr) ext2_test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) ext2_find_first_zero_bit(addr,size) diff --git a/include/asm-h8300/bitops.h b/include/asm-h8300/bitops.h index ff7c2b721594..af95f914e51c 100644 --- a/include/asm-h8300/bitops.h +++ b/include/asm-h8300/bitops.h @@ -397,9 +397,9 @@ found_middle: } /* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) __set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) diff --git a/include/asm-ia64/bitops.h b/include/asm-ia64/bitops.h index 36d0fb95ea89..eccb01c79c1a 100644 --- a/include/asm-ia64/bitops.h +++ b/include/asm-ia64/bitops.h @@ -394,18 +394,18 @@ extern int __find_next_bit(const void *addr, unsigned long size, #define __clear_bit(nr, addr) clear_bit(nr, addr) -#define ext2_set_bit test_and_set_bit +#define ext2_set_bit __test_and_set_bit #define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) -#define ext2_clear_bit test_and_clear_bit +#define ext2_clear_bit __test_and_clear_bit #define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) #define ext2_test_bit test_bit #define ext2_find_first_zero_bit find_first_zero_bit #define ext2_find_next_zero_bit find_next_zero_bit /* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) __set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) diff --git a/include/asm-m32r/bitops.h b/include/asm-m32r/bitops.h index abea2fdd8689..f8e993e0bbc0 100644 --- a/include/asm-m32r/bitops.h +++ b/include/asm-m32r/bitops.h @@ -575,7 +575,7 @@ found_middle: */ #ifdef __LITTLE_ENDIAN__ -#define ext2_set_bit test_and_set_bit +#define ext2_set_bit __test_and_set_bit #define ext2_clear_bit __test_and_clear_bit #define ext2_test_bit test_bit #define ext2_find_first_zero_bit find_first_zero_bit diff --git a/include/asm-m68k/bitops.h b/include/asm-m68k/bitops.h index 13f4c0048463..b7955b39d963 100644 --- a/include/asm-m68k/bitops.h +++ b/include/asm-m68k/bitops.h @@ -365,9 +365,9 @@ static inline int minix_find_first_zero_bit(const void *vaddr, unsigned size) return ((p - addr) << 4) + (res ^ 31); } -#define minix_test_and_set_bit(nr, addr) test_and_set_bit((nr) ^ 16, (unsigned long *)(addr)) -#define minix_set_bit(nr,addr) set_bit((nr) ^ 16, (unsigned long *)(addr)) -#define minix_test_and_clear_bit(nr, addr) test_and_clear_bit((nr) ^ 16, (unsigned long *)(addr)) +#define minix_test_and_set_bit(nr, addr) __test_and_set_bit((nr) ^ 16, (unsigned long *)(addr)) +#define minix_set_bit(nr,addr) __set_bit((nr) ^ 16, (unsigned long *)(addr)) +#define minix_test_and_clear_bit(nr, addr) __test_and_clear_bit((nr) ^ 16, (unsigned long *)(addr)) static inline int minix_test_bit(int nr, const void *vaddr) { @@ -377,9 +377,9 @@ static inline int minix_test_bit(int nr, const void *vaddr) /* Bitmap functions for the ext2 filesystem. */ -#define ext2_set_bit(nr, addr) test_and_set_bit((nr) ^ 24, (unsigned long *)(addr)) +#define ext2_set_bit(nr, addr) __test_and_set_bit((nr) ^ 24, (unsigned long *)(addr)) #define ext2_set_bit_atomic(lock, nr, addr) test_and_set_bit((nr) ^ 24, (unsigned long *)(addr)) -#define ext2_clear_bit(nr, addr) test_and_clear_bit((nr) ^ 24, (unsigned long *)(addr)) +#define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr) ^ 24, (unsigned long *)(addr)) #define ext2_clear_bit_atomic(lock, nr, addr) test_and_clear_bit((nr) ^ 24, (unsigned long *)(addr)) static inline int ext2_test_bit(int nr, const void *vaddr) diff --git a/include/asm-m68knommu/bitops.h b/include/asm-m68knommu/bitops.h index 25d8a3cfef90..00c4a2548e60 100644 --- a/include/asm-m68knommu/bitops.h +++ b/include/asm-m68knommu/bitops.h @@ -476,9 +476,9 @@ found_middle: } /* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) __set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) diff --git a/include/asm-mips/bitops.h b/include/asm-mips/bitops.h index 8e802059fe67..0e83abc829d4 100644 --- a/include/asm-mips/bitops.h +++ b/include/asm-mips/bitops.h @@ -962,9 +962,9 @@ found_middle: * FIXME: These assume that Minix uses the native byte/bitorder. * This limits the Minix filesystem's value for data exchange very much. */ -#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) __set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) diff --git a/include/asm-s390/bitops.h b/include/asm-s390/bitops.h index 3628899f48bb..6b6a01dfc8dc 100644 --- a/include/asm-s390/bitops.h +++ b/include/asm-s390/bitops.h @@ -871,11 +871,11 @@ static inline int sched_find_first_bit(unsigned long *b) */ #define ext2_set_bit(nr, addr) \ - test_and_set_bit((nr)^(__BITOPS_WORDSIZE - 8), (unsigned long *)addr) + __test_and_set_bit((nr)^(__BITOPS_WORDSIZE - 8), (unsigned long *)addr) #define ext2_set_bit_atomic(lock, nr, addr) \ test_and_set_bit((nr)^(__BITOPS_WORDSIZE - 8), (unsigned long *)addr) #define ext2_clear_bit(nr, addr) \ - test_and_clear_bit((nr)^(__BITOPS_WORDSIZE - 8), (unsigned long *)addr) + __test_and_clear_bit((nr)^(__BITOPS_WORDSIZE - 8), (unsigned long *)addr) #define ext2_clear_bit_atomic(lock, nr, addr) \ test_and_clear_bit((nr)^(__BITOPS_WORDSIZE - 8), (unsigned long *)addr) #define ext2_test_bit(nr, addr) \ @@ -1014,11 +1014,11 @@ ext2_find_next_zero_bit(void *vaddr, unsigned long size, unsigned long offset) /* Bitmap functions for the minix filesystem. */ /* FIXME !!! */ #define minix_test_and_set_bit(nr,addr) \ - test_and_set_bit(nr,(unsigned long *)addr) + __test_and_set_bit(nr,(unsigned long *)addr) #define minix_set_bit(nr,addr) \ - set_bit(nr,(unsigned long *)addr) + __set_bit(nr,(unsigned long *)addr) #define minix_test_and_clear_bit(nr,addr) \ - test_and_clear_bit(nr,(unsigned long *)addr) + __test_and_clear_bit(nr,(unsigned long *)addr) #define minix_test_bit(nr,addr) \ test_bit(nr,(unsigned long *)addr) #define minix_find_first_zero_bit(addr,size) \ diff --git a/include/asm-sh/bitops.h b/include/asm-sh/bitops.h index 1c5260860045..f8d504e7d9d6 100644 --- a/include/asm-sh/bitops.h +++ b/include/asm-sh/bitops.h @@ -339,8 +339,8 @@ static inline int sched_find_first_bit(const unsigned long *b) } #ifdef __LITTLE_ENDIAN__ -#define ext2_set_bit(nr, addr) test_and_set_bit((nr), (addr)) -#define ext2_clear_bit(nr, addr) test_and_clear_bit((nr), (addr)) +#define ext2_set_bit(nr, addr) __test_and_set_bit((nr), (addr)) +#define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr), (addr)) #define ext2_test_bit(nr, addr) test_bit((nr), (addr)) #define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr), (size)) #define ext2_find_next_zero_bit(addr, size, offset) \ @@ -349,30 +349,24 @@ static inline int sched_find_first_bit(const unsigned long *b) static __inline__ int ext2_set_bit(int nr, volatile void * addr) { int mask, retval; - unsigned long flags; volatile unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); - local_irq_save(flags); retval = (mask & *ADDR) != 0; *ADDR |= mask; - local_irq_restore(flags); return retval; } static __inline__ int ext2_clear_bit(int nr, volatile void * addr) { int mask, retval; - unsigned long flags; volatile unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); - local_irq_save(flags); retval = (mask & *ADDR) != 0; *ADDR &= ~mask; - local_irq_restore(flags); return retval; } @@ -459,9 +453,9 @@ found_middle: }) /* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) __set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) diff --git a/include/asm-sh64/bitops.h b/include/asm-sh64/bitops.h index ce9c3ad45fe0..5622b1a50cb1 100644 --- a/include/asm-sh64/bitops.h +++ b/include/asm-sh64/bitops.h @@ -382,8 +382,8 @@ static inline int sched_find_first_bit(unsigned long *b) #define hweight8(x) generic_hweight8(x) #ifdef __LITTLE_ENDIAN__ -#define ext2_set_bit(nr, addr) test_and_set_bit((nr), (addr)) -#define ext2_clear_bit(nr, addr) test_and_clear_bit((nr), (addr)) +#define ext2_set_bit(nr, addr) __test_and_set_bit((nr), (addr)) +#define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr), (addr)) #define ext2_test_bit(nr, addr) test_bit((nr), (addr)) #define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr), (size)) #define ext2_find_next_zero_bit(addr, size, offset) \ @@ -392,30 +392,24 @@ static inline int sched_find_first_bit(unsigned long *b) static __inline__ int ext2_set_bit(int nr, volatile void * addr) { int mask, retval; - unsigned long flags; volatile unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); - local_irq_save(flags); retval = (mask & *ADDR) != 0; *ADDR |= mask; - local_irq_restore(flags); return retval; } static __inline__ int ext2_clear_bit(int nr, volatile void * addr) { int mask, retval; - unsigned long flags; volatile unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); - local_irq_save(flags); retval = (mask & *ADDR) != 0; *ADDR &= ~mask; - local_irq_restore(flags); return retval; } @@ -502,9 +496,9 @@ found_middle: }) /* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) __set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) diff --git a/include/asm-sparc/bitops.h b/include/asm-sparc/bitops.h index 41722b5e45ef..f25109d62032 100644 --- a/include/asm-sparc/bitops.h +++ b/include/asm-sparc/bitops.h @@ -523,11 +523,11 @@ found_middle: /* Bitmap functions for the minix filesystem. */ #define minix_test_and_set_bit(nr,addr) \ - test_and_set_bit((nr),(unsigned long *)(addr)) + __test_and_set_bit((nr),(unsigned long *)(addr)) #define minix_set_bit(nr,addr) \ - set_bit((nr),(unsigned long *)(addr)) + __set_bit((nr),(unsigned long *)(addr)) #define minix_test_and_clear_bit(nr,addr) \ - test_and_clear_bit((nr),(unsigned long *)(addr)) + __test_and_clear_bit((nr),(unsigned long *)(addr)) #define minix_test_bit(nr,addr) \ test_bit((nr),(unsigned long *)(addr)) #define minix_find_first_zero_bit(addr,size) \ diff --git a/include/asm-sparc64/bitops.h b/include/asm-sparc64/bitops.h index 6efc0162fb09..2361f8736498 100644 --- a/include/asm-sparc64/bitops.h +++ b/include/asm-sparc64/bitops.h @@ -280,11 +280,11 @@ extern unsigned long find_next_zero_le_bit(unsigned long *, unsigned long, unsig /* Bitmap functions for the minix filesystem. */ #define minix_test_and_set_bit(nr,addr) \ - test_and_set_bit((nr),(unsigned long *)(addr)) + __test_and_set_bit((nr),(unsigned long *)(addr)) #define minix_set_bit(nr,addr) \ - set_bit((nr),(unsigned long *)(addr)) + __set_bit((nr),(unsigned long *)(addr)) #define minix_test_and_clear_bit(nr,addr) \ - test_and_clear_bit((nr),(unsigned long *)(addr)) + __test_and_clear_bit((nr),(unsigned long *)(addr)) #define minix_test_bit(nr,addr) \ test_bit((nr),(unsigned long *)(addr)) #define minix_find_first_zero_bit(addr,size) \ diff --git a/include/asm-v850/bitops.h b/include/asm-v850/bitops.h index 609b9e87222a..44d596e792e7 100644 --- a/include/asm-v850/bitops.h +++ b/include/asm-v850/bitops.h @@ -336,18 +336,18 @@ static inline int sched_find_first_bit(unsigned long *b) #define hweight16(x) generic_hweight16 (x) #define hweight8(x) generic_hweight8 (x) -#define ext2_set_bit test_and_set_bit +#define ext2_set_bit __test_and_set_bit #define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) -#define ext2_clear_bit test_and_clear_bit +#define ext2_clear_bit __test_and_clear_bit #define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) #define ext2_test_bit test_bit #define ext2_find_first_zero_bit find_first_zero_bit #define ext2_find_next_zero_bit find_next_zero_bit /* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit test_and_set_bit -#define minix_set_bit set_bit -#define minix_test_and_clear_bit test_and_clear_bit +#define minix_test_and_set_bit __test_and_set_bit +#define minix_set_bit __set_bit +#define minix_test_and_clear_bit __test_and_clear_bit #define minix_test_bit test_bit #define minix_find_first_zero_bit find_first_zero_bit diff --git a/include/asm-xtensa/bitops.h b/include/asm-xtensa/bitops.h index 0a2065f1a372..50b83726a497 100644 --- a/include/asm-xtensa/bitops.h +++ b/include/asm-xtensa/bitops.h @@ -436,9 +436,9 @@ static inline int sched_find_first_bit(const unsigned long *b) /* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) __set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) -- cgit v1.2.3 From 7a8a2429956fcfa3f3ef8fc105a4c055d70239ab Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:06 -0800 Subject: [PATCH] bitops: generic {,test_and_}{set,clear,change}_bit() This patch introduces the C-language equivalents of the functions below: void set_bit(int nr, volatile unsigned long *addr); void clear_bit(int nr, volatile unsigned long *addr); void change_bit(int nr, volatile unsigned long *addr); int test_and_set_bit(int nr, volatile unsigned long *addr); int test_and_clear_bit(int nr, volatile unsigned long *addr); int test_and_change_bit(int nr, volatile unsigned long *addr); In include/asm-generic/bitops/atomic.h This code largely copied from: include/asm-powerpc/bitops.h include/asm-parisc/bitops.h include/asm-parisc/atomic.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/atomic.h | 191 ++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 include/asm-generic/bitops/atomic.h (limited to 'include') diff --git a/include/asm-generic/bitops/atomic.h b/include/asm-generic/bitops/atomic.h new file mode 100644 index 000000000000..78339319ba02 --- /dev/null +++ b/include/asm-generic/bitops/atomic.h @@ -0,0 +1,191 @@ +#ifndef _ASM_GENERIC_BITOPS_ATOMIC_H_ +#define _ASM_GENERIC_BITOPS_ATOMIC_H_ + +#include + +#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +#ifdef CONFIG_SMP +#include +#include /* we use L1_CACHE_BYTES */ + +/* Use an array of spinlocks for our atomic_ts. + * Hash function to index into a different SPINLOCK. + * Since "a" is usually an address, use one spinlock per cacheline. + */ +# define ATOMIC_HASH_SIZE 4 +# define ATOMIC_HASH(a) (&(__atomic_hash[ (((unsigned long) a)/L1_CACHE_BYTES) & (ATOMIC_HASH_SIZE-1) ])) + +extern raw_spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] __lock_aligned; + +/* Can't use raw_spin_lock_irq because of #include problems, so + * this is the substitute */ +#define _atomic_spin_lock_irqsave(l,f) do { \ + raw_spinlock_t *s = ATOMIC_HASH(l); \ + local_irq_save(f); \ + __raw_spin_lock(s); \ +} while(0) + +#define _atomic_spin_unlock_irqrestore(l,f) do { \ + raw_spinlock_t *s = ATOMIC_HASH(l); \ + __raw_spin_unlock(s); \ + local_irq_restore(f); \ +} while(0) + + +#else +# define _atomic_spin_lock_irqsave(l,f) do { local_irq_save(f); } while (0) +# define _atomic_spin_unlock_irqrestore(l,f) do { local_irq_restore(f); } while (0) +#endif + +/* + * NMI events can occur at any time, including when interrupts have been + * disabled by *_irqsave(). So you can get NMI events occurring while a + * *_bit function is holding a spin lock. If the NMI handler also wants + * to do bit manipulation (and they do) then you can get a deadlock + * between the original caller of *_bit() and the NMI handler. + * + * by Keith Owens + */ + +/** + * set_bit - Atomically set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * This function is atomic and may not be reordered. See __set_bit() + * if you do not require the atomic guarantees. + * + * Note: there are no guarantees that this function will not be reordered + * on non x86 architectures, so if you are writting portable code, + * make sure not to rely on its reordering guarantees. + * + * Note that @nr may be almost arbitrarily large; this function is not + * restricted to acting on a single-word quantity. + */ +static inline void set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long flags; + + _atomic_spin_lock_irqsave(p, flags); + *p |= mask; + _atomic_spin_unlock_irqrestore(p, flags); +} + +/** + * clear_bit - Clears a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + * + * clear_bit() is atomic and may not be reordered. However, it does + * not contain a memory barrier, so if it is used for locking purposes, + * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() + * in order to ensure changes are visible on other processors. + */ +static inline void clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long flags; + + _atomic_spin_lock_irqsave(p, flags); + *p &= ~mask; + _atomic_spin_unlock_irqrestore(p, flags); +} + +/** + * change_bit - Toggle a bit in memory + * @nr: Bit to change + * @addr: Address to start counting from + * + * change_bit() is atomic and may not be reordered. It may be + * reordered on other architectures than x86. + * Note that @nr may be almost arbitrarily large; this function is not + * restricted to acting on a single-word quantity. + */ +static inline void change_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long flags; + + _atomic_spin_lock_irqsave(p, flags); + *p ^= mask; + _atomic_spin_unlock_irqrestore(p, flags); +} + +/** + * test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It may be reordered on other architectures than x86. + * It also implies a memory barrier. + */ +static inline int test_and_set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long old; + unsigned long flags; + + _atomic_spin_lock_irqsave(p, flags); + old = *p; + *p = old | mask; + _atomic_spin_unlock_irqrestore(p, flags); + + return (old & mask) != 0; +} + +/** + * test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to clear + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It can be reorderdered on other architectures other than x86. + * It also implies a memory barrier. + */ +static inline int test_and_clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long old; + unsigned long flags; + + _atomic_spin_lock_irqsave(p, flags); + old = *p; + *p = old & ~mask; + _atomic_spin_unlock_irqrestore(p, flags); + + return (old & mask) != 0; +} + +/** + * test_and_change_bit - Change a bit and return its old value + * @nr: Bit to change + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static inline int test_and_change_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long old; + unsigned long flags; + + _atomic_spin_lock_irqsave(p, flags); + old = *p; + *p = old ^ mask; + _atomic_spin_unlock_irqrestore(p, flags); + + return (old & mask) != 0; +} + +#endif /* _ASM_GENERIC_BITOPS_ATOMIC_H */ -- cgit v1.2.3 From 4117b02132d1cf96a3e1c57148e302c4801c974d Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:07 -0800 Subject: [PATCH] bitops: generic __{,test_and_}{set,clear,change}_bit() and test_bit() This patch introduces the C-language equivalents of the functions below: void __set_bit(int nr, volatile unsigned long *addr); void __clear_bit(int nr, volatile unsigned long *addr); void __change_bit(int nr, volatile unsigned long *addr); int __test_and_set_bit(int nr, volatile unsigned long *addr); int __test_and_clear_bit(int nr, volatile unsigned long *addr); int __test_and_change_bit(int nr, volatile unsigned long *addr); int test_bit(int nr, const volatile unsigned long *addr); In include/asm-generic/bitops/non-atomic.h This code largely copied from: asm-powerpc/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/non-atomic.h | 111 ++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 include/asm-generic/bitops/non-atomic.h (limited to 'include') diff --git a/include/asm-generic/bitops/non-atomic.h b/include/asm-generic/bitops/non-atomic.h new file mode 100644 index 000000000000..46a825cf2ae1 --- /dev/null +++ b/include/asm-generic/bitops/non-atomic.h @@ -0,0 +1,111 @@ +#ifndef _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ +#define _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ + +#include + +#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +/** + * __set_bit - Set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * Unlike set_bit(), this function is non-atomic and may be reordered. + * If it's called on the same region of memory simultaneously, the effect + * may be that only one operation succeeds. + */ +static inline void __set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + + *p |= mask; +} + +static inline void __clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + + *p &= ~mask; +} + +/** + * __change_bit - Toggle a bit in memory + * @nr: the bit to change + * @addr: the address to start counting from + * + * Unlike change_bit(), this function is non-atomic and may be reordered. + * If it's called on the same region of memory simultaneously, the effect + * may be that only one operation succeeds. + */ +static inline void __change_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + + *p ^= mask; +} + +/** + * __test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is non-atomic and can be reordered. + * If two examples of this operation race, one can appear to succeed + * but actually fail. You must protect multiple accesses with a lock. + */ +static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long old = *p; + + *p = old | mask; + return (old & mask) != 0; +} + +/** + * __test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to clear + * @addr: Address to count from + * + * This operation is non-atomic and can be reordered. + * If two examples of this operation race, one can appear to succeed + * but actually fail. You must protect multiple accesses with a lock. + */ +static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long old = *p; + + *p = old & ~mask; + return (old & mask) != 0; +} + +/* WARNING: non atomic and it can be reordered! */ +static inline int __test_and_change_bit(int nr, + volatile unsigned long *addr) +{ + unsigned long mask = BITOP_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long old = *p; + + *p = old ^ mask; + return (old & mask) != 0; +} + +/** + * test_bit - Determine whether a bit is set + * @nr: bit number to test + * @addr: Address to start counting from + */ +static inline int test_bit(int nr, const volatile unsigned long *addr) +{ + return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); +} + +#endif /* _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ */ -- cgit v1.2.3 From c1226a005ec400e966f4993dfcc0e99fd7baa6a1 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:08 -0800 Subject: [PATCH] bitops: generic __ffs() This patch introduces the C-language equivalent of the function: unsigned long __ffs(unsigned long word); In include/asm-generic/bitops/__ffs.h This code largely copied from: include/asm-sparc/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/__ffs.h | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 include/asm-generic/bitops/__ffs.h (limited to 'include') diff --git a/include/asm-generic/bitops/__ffs.h b/include/asm-generic/bitops/__ffs.h new file mode 100644 index 000000000000..9a3274aecf83 --- /dev/null +++ b/include/asm-generic/bitops/__ffs.h @@ -0,0 +1,43 @@ +#ifndef _ASM_GENERIC_BITOPS___FFS_H_ +#define _ASM_GENERIC_BITOPS___FFS_H_ + +#include + +/** + * __ffs - find first bit in word. + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static inline unsigned long __ffs(unsigned long word) +{ + int num = 0; + +#if BITS_PER_LONG == 64 + if ((word & 0xffffffff) == 0) { + num += 32; + word >>= 32; + } +#endif + if ((word & 0xffff) == 0) { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) { + num += 8; + word >>= 8; + } + if ((word & 0xf) == 0) { + num += 4; + word >>= 4; + } + if ((word & 0x3) == 0) { + num += 2; + word >>= 2; + } + if ((word & 0x1) == 0) + num += 1; + return num; +} + +#endif /* _ASM_GENERIC_BITOPS___FFS_H_ */ -- cgit v1.2.3 From 176d8b0c2709e764d491e63a0c1b3a3e1459fcf8 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:08 -0800 Subject: [PATCH] bitops: generic ffz() This patch introduces the C-language equivalent of the function: unsigned long ffz(unsigned long word); In include/asm-generic/bitops/ffz.h This code largely copied from: include/asm-parisc/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/ffz.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 include/asm-generic/bitops/ffz.h (limited to 'include') diff --git a/include/asm-generic/bitops/ffz.h b/include/asm-generic/bitops/ffz.h new file mode 100644 index 000000000000..6744bd4cdf46 --- /dev/null +++ b/include/asm-generic/bitops/ffz.h @@ -0,0 +1,12 @@ +#ifndef _ASM_GENERIC_BITOPS_FFZ_H_ +#define _ASM_GENERIC_BITOPS_FFZ_H_ + +/* + * ffz - find first zero in word. + * @word: The word to search + * + * Undefined if no zero exists, so code should check against ~0UL first. + */ +#define ffz(x) __ffs(~(x)) + +#endif /* _ASM_GENERIC_BITOPS_FFZ_H_ */ -- cgit v1.2.3 From 136abb32d6b4acf196425fb3968ebb368d84280e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:09 -0800 Subject: [PATCH] bitops: generic fls() This patch introduces the C-language equivalent of the function: int fls(int x); In include/asm-generic/bitops/fls.h This code largely copied from: include/linux/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/fls.h | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 include/asm-generic/bitops/fls.h (limited to 'include') diff --git a/include/asm-generic/bitops/fls.h b/include/asm-generic/bitops/fls.h new file mode 100644 index 000000000000..850859bc5069 --- /dev/null +++ b/include/asm-generic/bitops/fls.h @@ -0,0 +1,41 @@ +#ifndef _ASM_GENERIC_BITOPS_FLS_H_ +#define _ASM_GENERIC_BITOPS_FLS_H_ + +/** + * fls - find last (most-significant) bit set + * @x: the word to search + * + * This is defined the same way as ffs. + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ + +static inline int fls(int x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xffff0000u)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xff000000u)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf0000000u)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc0000000u)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000u)) { + x <<= 1; + r -= 1; + } + return r; +} + +#endif /* _ASM_GENERIC_BITOPS_FLS_H_ */ -- cgit v1.2.3 From 2dfc383ad587bbead84739a9ff9273df3eda983d Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:10 -0800 Subject: [PATCH] bitops: generic fls64() This patch introduces the C-language equivalent of the function: int fls64(__u64 x); In include/asm-generic/bitops/fls64.h This code largely copied from: include/linux/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/fls64.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 include/asm-generic/bitops/fls64.h (limited to 'include') diff --git a/include/asm-generic/bitops/fls64.h b/include/asm-generic/bitops/fls64.h new file mode 100644 index 000000000000..716c51e0dd4c --- /dev/null +++ b/include/asm-generic/bitops/fls64.h @@ -0,0 +1,12 @@ +#ifndef _ASM_GENERIC_BITOPS_FLS64_H_ +#define _ASM_GENERIC_BITOPS_FLS64_H_ + +static inline int fls64(__u64 x) +{ + __u32 h = x >> 32; + if (h) + return fls(h) + 32; + return fls(x); +} + +#endif /* _ASM_GENERIC_BITOPS_FLS64_H_ */ -- cgit v1.2.3 From c7f612cdf091def01454e7e132c7d7a3f419fbc4 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:11 -0800 Subject: [PATCH] bitops: generic find_{next,first}{,_zero}_bit() This patch introduces the C-language equivalents of the functions below: unsigned logn find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset); unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, unsigned long offset); unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size); unsigned long find_first_bit(const unsigned long *addr, unsigned long size); In include/asm-generic/bitops/find.h This code largely copied from: arch/powerpc/lib/bitops.c Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/find.h | 13 +++++ lib/find_next_bit.c | 112 +++++++++++++++++++++++++++----------- 2 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 include/asm-generic/bitops/find.h (limited to 'include') diff --git a/include/asm-generic/bitops/find.h b/include/asm-generic/bitops/find.h new file mode 100644 index 000000000000..72a51e5a12ef --- /dev/null +++ b/include/asm-generic/bitops/find.h @@ -0,0 +1,13 @@ +#ifndef _ASM_GENERIC_BITOPS_FIND_H_ +#define _ASM_GENERIC_BITOPS_FIND_H_ + +extern unsigned long find_next_bit(const unsigned long *addr, unsigned long + size, unsigned long offset); + +extern unsigned long find_next_zero_bit(const unsigned long *addr, unsigned + long size, unsigned long offset); + +#define find_first_bit(addr, size) find_next_bit((addr), (size), 0) +#define find_first_zero_bit(addr, size) find_next_zero_bit((addr), (size), 0) + +#endif /*_ASM_GENERIC_BITOPS_FIND_H_ */ diff --git a/lib/find_next_bit.c b/lib/find_next_bit.c index c05b4b19cf6c..9c90853b4472 100644 --- a/lib/find_next_bit.c +++ b/lib/find_next_bit.c @@ -11,48 +11,98 @@ #include #include +#include -int find_next_bit(const unsigned long *addr, int size, int offset) +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +/** + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +unsigned long find_next_bit(const unsigned long *addr, unsigned long size, + unsigned long offset) { - const unsigned long *base; - const int NBITS = sizeof(*addr) * 8; + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); unsigned long tmp; - base = addr; + if (offset >= size) + return size; + size -= result; + offset %= BITS_PER_LONG; if (offset) { - int suboffset; - - addr += offset / NBITS; - - suboffset = offset % NBITS; - if (suboffset) { - tmp = *addr; - tmp >>= suboffset; - if (tmp) - goto finish; - } - - addr++; + tmp = *(p++); + tmp &= (~0UL << offset); + if (size < BITS_PER_LONG) + goto found_first; + if (tmp) + goto found_middle; + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) { + if ((tmp = *(p++))) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; } + if (!size) + return result; + tmp = *p; - while ((tmp = *addr) == 0) - addr++; +found_first: + tmp &= (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __ffs(tmp); +} - offset = (addr - base) * NBITS; +EXPORT_SYMBOL(find_next_bit); - finish: - /* count the remaining bits without using __ffs() since that takes a 32-bit arg */ - while (!(tmp & 0xff)) { - offset += 8; - tmp >>= 8; - } +/* + * This implementation of find_{first,next}_zero_bit was stolen from + * Linus' asm-alpha/bitops.h. + */ +unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, + unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; - while (!(tmp & 1)) { - offset++; - tmp >>= 1; + if (offset >= size) + return size; + size -= result; + offset %= BITS_PER_LONG; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (BITS_PER_LONG - offset); + if (size < BITS_PER_LONG) + goto found_first; + if (~tmp) + goto found_middle; + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) { + if (~(tmp = *(p++))) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; } + if (!size) + return result; + tmp = *p; - return offset; +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. */ +found_middle: + return result + ffz(tmp); } -EXPORT_SYMBOL(find_next_bit); +EXPORT_SYMBOL(find_next_zero_bit); -- cgit v1.2.3 From 6d29ea23da033f46ac4ab359d46650a7f7474611 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:12 -0800 Subject: [PATCH] bitops: generic sched_find_first_bit() This patch introduces the C-language equivalent of the function: int sched_find_first_bit(const unsigned long *b); In include/asm-generic/bitops/sched.h This code largely copied from: include/asm-powerpc/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/sched.h | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 include/asm-generic/bitops/sched.h (limited to 'include') diff --git a/include/asm-generic/bitops/sched.h b/include/asm-generic/bitops/sched.h new file mode 100644 index 000000000000..5ef93a4d009f --- /dev/null +++ b/include/asm-generic/bitops/sched.h @@ -0,0 +1,36 @@ +#ifndef _ASM_GENERIC_BITOPS_SCHED_H_ +#define _ASM_GENERIC_BITOPS_SCHED_H_ + +#include /* unlikely() */ +#include + +/* + * Every architecture must define this function. It's the fastest + * way of searching a 140-bit bitmap where the first 100 bits are + * unlikely to be set. It's guaranteed that at least one of the 140 + * bits is cleared. + */ +static inline int sched_find_first_bit(const unsigned long *b) +{ +#if BITS_PER_LONG == 64 + if (unlikely(b[0])) + return __ffs(b[0]); + if (unlikely(b[1])) + return __ffs(b[1]) + 64; + return __ffs(b[2]) + 128; +#elif BITS_PER_LONG == 32 + if (unlikely(b[0])) + return __ffs(b[0]); + if (unlikely(b[1])) + return __ffs(b[1]) + 32; + if (unlikely(b[2])) + return __ffs(b[2]) + 64; + if (b[3]) + return __ffs(b[3]) + 96; + return __ffs(b[4]) + 128; +#else +#error BITS_PER_LONG not defined +#endif +} + +#endif /* _ASM_GENERIC_BITOPS_SCHED_H_ */ -- cgit v1.2.3 From 09020adb61416c4307de35941a9725a5e33d9beb Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:12 -0800 Subject: [PATCH] bitops: generic ffs() This patch introduces the C-language equivalent of the function: int ffs(int x); In include/asm-generic/bitops/ffs.h This code largely copied from: include/linux/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/ffs.h | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 include/asm-generic/bitops/ffs.h (limited to 'include') diff --git a/include/asm-generic/bitops/ffs.h b/include/asm-generic/bitops/ffs.h new file mode 100644 index 000000000000..fbbb43af7dc0 --- /dev/null +++ b/include/asm-generic/bitops/ffs.h @@ -0,0 +1,41 @@ +#ifndef _ASM_GENERIC_BITOPS_FFS_H_ +#define _ASM_GENERIC_BITOPS_FFS_H_ + +/** + * ffs - find first bit set + * @x: the word to search + * + * This is defined the same way as + * the libc and compiler builtin ffs routines, therefore + * differs in spirit from the above ffz (man ffs). + */ +static inline int ffs(int x) +{ + int r = 1; + + if (!x) + return 0; + if (!(x & 0xffff)) { + x >>= 16; + r += 16; + } + if (!(x & 0xff)) { + x >>= 8; + r += 8; + } + if (!(x & 0xf)) { + x >>= 4; + r += 4; + } + if (!(x & 3)) { + x >>= 2; + r += 2; + } + if (!(x & 1)) { + x >>= 1; + r += 1; + } + return r; +} + +#endif /* _ASM_GENERIC_BITOPS_FFS_H_ */ -- cgit v1.2.3 From 3b9ed1a5d2d121f32d2cb4f2b05f1fc57c99c946 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:13 -0800 Subject: [PATCH] bitops: generic hweight{64,32,16,8}() This patch introduces the C-language equivalents of the functions below: unsigned int hweight32(unsigned int w); unsigned int hweight16(unsigned int w); unsigned int hweight8(unsigned int w); unsigned long hweight64(__u64 w); In include/asm-generic/bitops/hweight.h This code largely copied from: include/linux/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/hweight.h | 9 ++++++ lib/Makefile | 1 + lib/hweight.c | 54 ++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 include/asm-generic/bitops/hweight.h create mode 100644 lib/hweight.c (limited to 'include') diff --git a/include/asm-generic/bitops/hweight.h b/include/asm-generic/bitops/hweight.h new file mode 100644 index 000000000000..8be6f17d6a5c --- /dev/null +++ b/include/asm-generic/bitops/hweight.h @@ -0,0 +1,9 @@ +#ifndef _ASM_GENERIC_BITOPS_HWEIGHT_H_ +#define _ASM_GENERIC_BITOPS_HWEIGHT_H_ + +extern unsigned int hweight32(unsigned int w); +extern unsigned int hweight16(unsigned int w); +extern unsigned int hweight8(unsigned int w); +extern unsigned long hweight64(__u64 w); + +#endif /* _ASM_GENERIC_BITOPS_HWEIGHT_H_ */ diff --git a/lib/Makefile b/lib/Makefile index f827e3c24ec0..b830c9a15541 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -23,6 +23,7 @@ lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o lib-$(CONFIG_SEMAPHORE_SLEEPERS) += semaphore-sleepers.o lib-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o +lib-$(CONFIG_GENERIC_HWEIGHT) += hweight.o obj-$(CONFIG_LOCK_KERNEL) += kernel_lock.o obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o diff --git a/lib/hweight.c b/lib/hweight.c new file mode 100644 index 000000000000..721a4b8b4fb8 --- /dev/null +++ b/lib/hweight.c @@ -0,0 +1,54 @@ +#include +#include + +/** + * hweightN - returns the hamming weight of a N-bit word + * @x: the word to weigh + * + * The Hamming Weight of a number is the total number of bits set in it. + */ + +unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} +EXPORT_SYMBOL(hweight32); + +unsigned int hweight16(unsigned int w) +{ + unsigned int res = (w & 0x5555) + ((w >> 1) & 0x5555); + res = (res & 0x3333) + ((res >> 2) & 0x3333); + res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F); + return (res & 0x00FF) + ((res >> 8) & 0x00FF); +} +EXPORT_SYMBOL(hweight16); + +unsigned int hweight8(unsigned int w) +{ + unsigned int res = (w & 0x55) + ((w >> 1) & 0x55); + res = (res & 0x33) + ((res >> 2) & 0x33); + return (res & 0x0F) + ((res >> 4) & 0x0F); +} +EXPORT_SYMBOL(hweight8); + +unsigned long hweight64(__u64 w) +{ +#if BITS_PER_LONG == 32 + return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w); +#elif BITS_PER_LONG == 64 + u64 res; + res = (w & 0x5555555555555555ul) + ((w >> 1) & 0x5555555555555555ul); + res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul); + res = (res & 0x0F0F0F0F0F0F0F0Ful) + ((res >> 4) & 0x0F0F0F0F0F0F0F0Ful); + res = (res & 0x00FF00FF00FF00FFul) + ((res >> 8) & 0x00FF00FF00FF00FFul); + res = (res & 0x0000FFFF0000FFFFul) + ((res >> 16) & 0x0000FFFF0000FFFFul); + return (res & 0x00000000FFFFFFFFul) + ((res >> 32) & 0x00000000FFFFFFFFul); +#else +#error BITS_PER_LONG not defined +#endif +} +EXPORT_SYMBOL(hweight64); -- cgit v1.2.3 From a54baa1487c51c8306dd6f457c1b5d5fcd539fff Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:14 -0800 Subject: [PATCH] fix error: __u32 undeclared Build fix for s390 declare __u32 and __u64. Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/fls64.h | 2 ++ include/asm-generic/bitops/hweight.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/asm-generic/bitops/fls64.h b/include/asm-generic/bitops/fls64.h index 716c51e0dd4c..1b6b17ce2428 100644 --- a/include/asm-generic/bitops/fls64.h +++ b/include/asm-generic/bitops/fls64.h @@ -1,6 +1,8 @@ #ifndef _ASM_GENERIC_BITOPS_FLS64_H_ #define _ASM_GENERIC_BITOPS_FLS64_H_ +#include + static inline int fls64(__u64 x) { __u32 h = x >> 32; diff --git a/include/asm-generic/bitops/hweight.h b/include/asm-generic/bitops/hweight.h index 8be6f17d6a5c..fbbc383771da 100644 --- a/include/asm-generic/bitops/hweight.h +++ b/include/asm-generic/bitops/hweight.h @@ -1,6 +1,8 @@ #ifndef _ASM_GENERIC_BITOPS_HWEIGHT_H_ #define _ASM_GENERIC_BITOPS_HWEIGHT_H_ +#include + extern unsigned int hweight32(unsigned int w); extern unsigned int hweight16(unsigned int w); extern unsigned int hweight8(unsigned int w); -- cgit v1.2.3 From 930ae745f50088279fdc06057a429f16495b53a2 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:15 -0800 Subject: [PATCH] bitops: generic ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() This patch introduces the C-language equivalents of the functions below: int ext2_set_bit(int nr, volatile unsigned long *addr); int ext2_clear_bit(int nr, volatile unsigned long *addr); int ext2_test_bit(int nr, const volatile unsigned long *addr); unsigned long ext2_find_first_zero_bit(const unsigned long *addr, unsigned long size); unsinged long ext2_find_next_zero_bit(const unsigned long *addr, unsigned long size); In include/asm-generic/bitops/ext2-non-atomic.h This code largely copied from: include/asm-powerpc/bitops.h include/asm-parisc/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/ext2-non-atomic.h | 18 +++++++ include/asm-generic/bitops/le.h | 53 ++++++++++++++++++++ lib/find_next_bit.c | 73 ++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 include/asm-generic/bitops/ext2-non-atomic.h create mode 100644 include/asm-generic/bitops/le.h (limited to 'include') diff --git a/include/asm-generic/bitops/ext2-non-atomic.h b/include/asm-generic/bitops/ext2-non-atomic.h new file mode 100644 index 000000000000..1697404afa05 --- /dev/null +++ b/include/asm-generic/bitops/ext2-non-atomic.h @@ -0,0 +1,18 @@ +#ifndef _ASM_GENERIC_BITOPS_EXT2_NON_ATOMIC_H_ +#define _ASM_GENERIC_BITOPS_EXT2_NON_ATOMIC_H_ + +#include + +#define ext2_set_bit(nr,addr) \ + generic___test_and_set_le_bit((nr),(unsigned long *)(addr)) +#define ext2_clear_bit(nr,addr) \ + generic___test_and_clear_le_bit((nr),(unsigned long *)(addr)) + +#define ext2_test_bit(nr,addr) \ + generic_test_le_bit((nr),(unsigned long *)(addr)) +#define ext2_find_first_zero_bit(addr, size) \ + generic_find_first_zero_le_bit((unsigned long *)(addr), (size)) +#define ext2_find_next_zero_bit(addr, size, off) \ + generic_find_next_zero_le_bit((unsigned long *)(addr), (size), (off)) + +#endif /* _ASM_GENERIC_BITOPS_EXT2_NON_ATOMIC_H_ */ diff --git a/include/asm-generic/bitops/le.h b/include/asm-generic/bitops/le.h new file mode 100644 index 000000000000..b9c7e5d2d2ad --- /dev/null +++ b/include/asm-generic/bitops/le.h @@ -0,0 +1,53 @@ +#ifndef _ASM_GENERIC_BITOPS_LE_H_ +#define _ASM_GENERIC_BITOPS_LE_H_ + +#include +#include + +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) +#define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7) + +#if defined(__LITTLE_ENDIAN) + +#define generic_test_le_bit(nr, addr) test_bit(nr, addr) +#define generic___set_le_bit(nr, addr) __set_bit(nr, addr) +#define generic___clear_le_bit(nr, addr) __clear_bit(nr, addr) + +#define generic_test_and_set_le_bit(nr, addr) test_and_set_bit(nr, addr) +#define generic_test_and_clear_le_bit(nr, addr) test_and_clear_bit(nr, addr) + +#define generic___test_and_set_le_bit(nr, addr) __test_and_set_bit(nr, addr) +#define generic___test_and_clear_le_bit(nr, addr) __test_and_clear_bit(nr, addr) + +#define generic_find_next_zero_le_bit(addr, size, offset) find_next_zero_bit(addr, size, offset) + +#elif defined(__BIG_ENDIAN) + +#define generic_test_le_bit(nr, addr) \ + test_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) +#define generic___set_le_bit(nr, addr) \ + __set_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) +#define generic___clear_le_bit(nr, addr) \ + __clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) + +#define generic_test_and_set_le_bit(nr, addr) \ + test_and_set_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) +#define generic_test_and_clear_le_bit(nr, addr) \ + test_and_clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) + +#define generic___test_and_set_le_bit(nr, addr) \ + __test_and_set_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) +#define generic___test_and_clear_le_bit(nr, addr) \ + __test_and_clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) + +extern unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, + unsigned long size, unsigned long offset); + +#else +#error "Please fix " +#endif + +#define generic_find_first_zero_le_bit(addr, size) \ + generic_find_next_zero_le_bit((addr), (size), 0) + +#endif /* _ASM_GENERIC_BITOPS_LE_H_ */ diff --git a/lib/find_next_bit.c b/lib/find_next_bit.c index 9c90853b4472..bda0d71a2514 100644 --- a/lib/find_next_bit.c +++ b/lib/find_next_bit.c @@ -12,6 +12,7 @@ #include #include #include +#include #define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) @@ -106,3 +107,75 @@ found_middle: } EXPORT_SYMBOL(find_next_zero_bit); + +#ifdef __BIG_ENDIAN + +/* include/linux/byteorder does not support "unsigned long" type */ +static inline unsigned long ext2_swabp(const unsigned long * x) +{ +#if BITS_PER_LONG == 64 + return (unsigned long) __swab64p((u64 *) x); +#elif BITS_PER_LONG == 32 + return (unsigned long) __swab32p((u32 *) x); +#else +#error BITS_PER_LONG not defined +#endif +} + +/* include/linux/byteorder doesn't support "unsigned long" type */ +static inline unsigned long ext2_swab(const unsigned long y) +{ +#if BITS_PER_LONG == 64 + return (unsigned long) __swab64((u64) y); +#elif BITS_PER_LONG == 32 + return (unsigned long) __swab32((u32) y); +#else +#error BITS_PER_LONG not defined +#endif +} + +unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned + long size, unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG - 1); + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= (BITS_PER_LONG - 1UL); + if (offset) { + tmp = ext2_swabp(p++); + tmp |= (~0UL >> (BITS_PER_LONG - offset)); + if (size < BITS_PER_LONG) + goto found_first; + if (~tmp) + goto found_middle; + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + + while (size & ~(BITS_PER_LONG - 1)) { + if (~(tmp = *(p++))) + goto found_middle_swap; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = ext2_swabp(p); +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. Skip ffz */ +found_middle: + return result + ffz(tmp); + +found_middle_swap: + return result + ffz(ext2_swab(tmp)); +} + +EXPORT_SYMBOL(generic_find_next_zero_le_bit); + +#endif /* __BIG_ENDIAN */ -- cgit v1.2.3 From 765f34fe324bdf0c0544b3404d25aaa511e3834b Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:16 -0800 Subject: [PATCH] bitops: generic ext2_{set,clear}_bit_atomic() This patch introduces the C-language equivalents of the functions below: int ext2_set_bit_atomic(int nr, volatile unsigned long *addr); int ext2_clear_bit_atomic(int nr, volatile unsigned long *addr); In include/asm-generic/bitops/ext2-atomic.h This code largely copied from: include/asm-sparc/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/ext2-atomic.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 include/asm-generic/bitops/ext2-atomic.h (limited to 'include') diff --git a/include/asm-generic/bitops/ext2-atomic.h b/include/asm-generic/bitops/ext2-atomic.h new file mode 100644 index 000000000000..ab1c875efb74 --- /dev/null +++ b/include/asm-generic/bitops/ext2-atomic.h @@ -0,0 +1,22 @@ +#ifndef _ASM_GENERIC_BITOPS_EXT2_ATOMIC_H_ +#define _ASM_GENERIC_BITOPS_EXT2_ATOMIC_H_ + +#define ext2_set_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (unsigned long *)(addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (unsigned long *)(addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#endif /* _ASM_GENERIC_BITOPS_EXT2_ATOMIC_H_ */ -- cgit v1.2.3 From b1bb9522daf2fb49c5793d128023e9ca1e08e13b Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:17 -0800 Subject: [PATCH] bitops: generic minix_{test,set,test_and_clear,test,find_first_zero}_bit() This patch introduces the C-language equivalents of the functions below: int minix_test_and_set_bit(int nr, volatile unsigned long *addr); int minix_set_bit(int nr, volatile unsigned long *addr); int minix_test_and_clear_bit(int nr, volatile unsigned long *addr); int minix_test_bit(int nr, const volatile unsigned long *addr); unsigned long minix_find_first_zero_bit(const unsigned long *addr, unsigned long size); In include/asm-generic/bitops/minix.h and include/asm-generic/bitops/minix-le.h This code largely copied from: include/asm-sparc/bitops.h Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops/minix-le.h | 17 +++++++++++++++++ include/asm-generic/bitops/minix.h | 15 +++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 include/asm-generic/bitops/minix-le.h create mode 100644 include/asm-generic/bitops/minix.h (limited to 'include') diff --git a/include/asm-generic/bitops/minix-le.h b/include/asm-generic/bitops/minix-le.h new file mode 100644 index 000000000000..4a981c1bb1ae --- /dev/null +++ b/include/asm-generic/bitops/minix-le.h @@ -0,0 +1,17 @@ +#ifndef _ASM_GENERIC_BITOPS_MINIX_LE_H_ +#define _ASM_GENERIC_BITOPS_MINIX_LE_H_ + +#include + +#define minix_test_and_set_bit(nr,addr) \ + generic___test_and_set_le_bit((nr),(unsigned long *)(addr)) +#define minix_set_bit(nr,addr) \ + generic___set_le_bit((nr),(unsigned long *)(addr)) +#define minix_test_and_clear_bit(nr,addr) \ + generic___test_and_clear_le_bit((nr),(unsigned long *)(addr)) +#define minix_test_bit(nr,addr) \ + generic_test_le_bit((nr),(unsigned long *)(addr)) +#define minix_find_first_zero_bit(addr,size) \ + generic_find_first_zero_le_bit((unsigned long *)(addr),(size)) + +#endif /* _ASM_GENERIC_BITOPS_MINIX_LE_H_ */ diff --git a/include/asm-generic/bitops/minix.h b/include/asm-generic/bitops/minix.h new file mode 100644 index 000000000000..91f42e87aa51 --- /dev/null +++ b/include/asm-generic/bitops/minix.h @@ -0,0 +1,15 @@ +#ifndef _ASM_GENERIC_BITOPS_MINIX_H_ +#define _ASM_GENERIC_BITOPS_MINIX_H_ + +#define minix_test_and_set_bit(nr,addr) \ + __test_and_set_bit((nr),(unsigned long *)(addr)) +#define minix_set_bit(nr,addr) \ + __set_bit((nr),(unsigned long *)(addr)) +#define minix_test_and_clear_bit(nr,addr) \ + __test_and_clear_bit((nr),(unsigned long *)(addr)) +#define minix_test_bit(nr,addr) \ + test_bit((nr),(unsigned long *)(addr)) +#define minix_find_first_zero_bit(addr,size) \ + find_first_zero_bit((unsigned long *)(addr),(size)) + +#endif /* _ASM_GENERIC_BITOPS_MINIX_H_ */ -- cgit v1.2.3 From f7c29678739e69b8298496f926e87e5991e745e8 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:18 -0800 Subject: [PATCH] bitops: alpha: use generic bitops - unless defined(__alpha_cix__) and defined(__alpha_fix__) - remove generic_fls() - remove generic_hweight{64,32,16,8}() - remove generic_fls64() - remove find_{next,first}{,_zero}_bit() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: Richard Henderson Cc: Ivan Kokshaysky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/Kconfig | 8 +++ include/asm-alpha/bitops.h | 123 +++------------------------------------------ 2 files changed, 15 insertions(+), 116 deletions(-) (limited to 'include') diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index eedf41bf7057..9bef61b30367 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -25,6 +25,10 @@ config RWSEM_XCHGADD_ALGORITHM bool default y +config GENERIC_FIND_NEXT_BIT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y @@ -447,6 +451,10 @@ config ALPHA_IRONGATE depends on ALPHA_NAUTILUS default y +config GENERIC_HWEIGHT + bool + default y if !ALPHA_EV6 && !ALPHA_EV67 + config ALPHA_AVANTI bool depends on ALPHA_XL || ALPHA_AVANTI_CH diff --git a/include/asm-alpha/bitops.h b/include/asm-alpha/bitops.h index e3e602fe5060..3f88715e811e 100644 --- a/include/asm-alpha/bitops.h +++ b/include/asm-alpha/bitops.h @@ -319,9 +319,9 @@ static inline int fls(int word) return 64 - __kernel_ctlz(word & 0xffffffff); } #else -#define fls generic_fls +#include #endif -#define fls64 generic_fls64 +#include /* Compute powers of two for the given integer. */ static inline long floor_log2(unsigned long word) @@ -358,112 +358,12 @@ static inline unsigned long hweight64(unsigned long w) #define hweight16(x) (unsigned int) hweight64((x) & 0xfffful) #define hweight8(x) (unsigned int) hweight64((x) & 0xfful) #else -static inline unsigned long hweight64(unsigned long w) -{ - unsigned long result; - for (result = 0; w ; w >>= 1) - result += (w & 1); - return result; -} - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include #endif #endif /* __KERNEL__ */ -/* - * Find next zero bit in a bitmap reasonably efficiently.. - */ -static inline unsigned long -find_next_zero_bit(const void *addr, unsigned long size, unsigned long offset) -{ - const unsigned long *p = addr; - unsigned long result = offset & ~63UL; - unsigned long tmp; - - p += offset >> 6; - if (offset >= size) - return size; - size -= result; - offset &= 63UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (64-offset); - if (size < 64) - goto found_first; - if (~tmp) - goto found_middle; - size -= 64; - result += 64; - } - while (size & ~63UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 64; - size -= 64; - } - if (!size) - return result; - tmp = *p; - found_first: - tmp |= ~0UL << size; - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. */ - found_middle: - return result + ffz(tmp); -} - -/* - * Find next one bit in a bitmap reasonably efficiently. - */ -static inline unsigned long -find_next_bit(const void * addr, unsigned long size, unsigned long offset) -{ - const unsigned long *p = addr; - unsigned long result = offset & ~63UL; - unsigned long tmp; - - p += offset >> 6; - if (offset >= size) - return size; - size -= result; - offset &= 63UL; - if (offset) { - tmp = *(p++); - tmp &= ~0UL << offset; - if (size < 64) - goto found_first; - if (tmp) - goto found_middle; - size -= 64; - result += 64; - } - while (size & ~63UL) { - if ((tmp = *(p++))) - goto found_middle; - result += 64; - size -= 64; - } - if (!size) - return result; - tmp = *p; - found_first: - tmp &= ~0UL >> (64 - size); - if (!tmp) - return result + size; - found_middle: - return result + __ffs(tmp); -} - -/* - * The optimizer actually does good code for this case. - */ -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) +#include #ifdef __KERNEL__ @@ -487,21 +387,12 @@ sched_find_first_bit(unsigned long b[3]) return __ffs(b0) + ofs; } +#include -#define ext2_set_bit __test_and_set_bit #define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) -#define ext2_clear_bit __test_and_clear_bit #define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) -#define ext2_test_bit test_bit -#define ext2_find_first_zero_bit find_first_zero_bit -#define ext2_find_next_zero_bit find_next_zero_bit - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) __set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) + +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From b89c3b165fbec605c60fd5a9e32d647e4c0befbb Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:19 -0800 Subject: [PATCH] bitops: arm: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - if __LINUX_ARM_ARCH__ < 5 - remove ffz() - remove __ffs() - remove generic_fls() - remove generic_ffs() - remove generic_fls64() - remove sched_find_first_bit() - remove generic_hweight{32,16,8}() Signed-off-by: Akinobu Mita Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/Kconfig | 4 ++ include/asm-arm/bitops.h | 146 ++++------------------------------------------- 2 files changed, 14 insertions(+), 136 deletions(-) (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 0dd24ebdf6ac..bf2e72698d02 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -53,6 +53,10 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-arm/bitops.h b/include/asm-arm/bitops.h index eaecd553e856..0ac54b1a8bad 100644 --- a/include/asm-arm/bitops.h +++ b/include/asm-arm/bitops.h @@ -117,65 +117,7 @@ ____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p) return res & mask; } -/* - * Now the non-atomic variants. We let the compiler handle all - * optimisations for these. These are all _native_ endian. - */ -static inline void __set_bit(int nr, volatile unsigned long *p) -{ - p[nr >> 5] |= (1UL << (nr & 31)); -} - -static inline void __clear_bit(int nr, volatile unsigned long *p) -{ - p[nr >> 5] &= ~(1UL << (nr & 31)); -} - -static inline void __change_bit(int nr, volatile unsigned long *p) -{ - p[nr >> 5] ^= (1UL << (nr & 31)); -} - -static inline int __test_and_set_bit(int nr, volatile unsigned long *p) -{ - unsigned long oldval, mask = 1UL << (nr & 31); - - p += nr >> 5; - - oldval = *p; - *p = oldval | mask; - return oldval & mask; -} - -static inline int __test_and_clear_bit(int nr, volatile unsigned long *p) -{ - unsigned long oldval, mask = 1UL << (nr & 31); - - p += nr >> 5; - - oldval = *p; - *p = oldval & ~mask; - return oldval & mask; -} - -static inline int __test_and_change_bit(int nr, volatile unsigned long *p) -{ - unsigned long oldval, mask = 1UL << (nr & 31); - - p += nr >> 5; - - oldval = *p; - *p = oldval ^ mask; - return oldval & mask; -} - -/* - * This routine doesn't need to be atomic. - */ -static inline int __test_bit(int nr, const volatile unsigned long * p) -{ - return (p[nr >> 5] >> (nr & 31)) & 1UL; -} +#include /* * A note about Endian-ness. @@ -261,7 +203,6 @@ extern int _find_next_bit_be(const unsigned long *p, int size, int offset); #define test_and_set_bit(nr,p) ATOMIC_BITOP_LE(test_and_set_bit,nr,p) #define test_and_clear_bit(nr,p) ATOMIC_BITOP_LE(test_and_clear_bit,nr,p) #define test_and_change_bit(nr,p) ATOMIC_BITOP_LE(test_and_change_bit,nr,p) -#define test_bit(nr,p) __test_bit(nr,p) #define find_first_zero_bit(p,sz) _find_first_zero_bit_le(p,sz) #define find_next_zero_bit(p,sz,off) _find_next_zero_bit_le(p,sz,off) #define find_first_bit(p,sz) _find_first_bit_le(p,sz) @@ -280,7 +221,6 @@ extern int _find_next_bit_be(const unsigned long *p, int size, int offset); #define test_and_set_bit(nr,p) ATOMIC_BITOP_BE(test_and_set_bit,nr,p) #define test_and_clear_bit(nr,p) ATOMIC_BITOP_BE(test_and_clear_bit,nr,p) #define test_and_change_bit(nr,p) ATOMIC_BITOP_BE(test_and_change_bit,nr,p) -#define test_bit(nr,p) __test_bit(nr,p) #define find_first_zero_bit(p,sz) _find_first_zero_bit_be(p,sz) #define find_next_zero_bit(p,sz,off) _find_next_zero_bit_be(p,sz,off) #define find_first_bit(p,sz) _find_first_bit_be(p,sz) @@ -292,55 +232,10 @@ extern int _find_next_bit_be(const unsigned long *p, int size, int offset); #if __LINUX_ARM_ARCH__ < 5 -/* - * ffz = Find First Zero in word. Undefined if no zero exists, - * so code should check against ~0UL first.. - */ -static inline unsigned long ffz(unsigned long word) -{ - int k; - - word = ~word; - k = 31; - if (word & 0x0000ffff) { k -= 16; word <<= 16; } - if (word & 0x00ff0000) { k -= 8; word <<= 8; } - if (word & 0x0f000000) { k -= 4; word <<= 4; } - if (word & 0x30000000) { k -= 2; word <<= 2; } - if (word & 0x40000000) { k -= 1; } - return k; -} - -/* - * ffz = Find First Zero in word. Undefined if no zero exists, - * so code should check against ~0UL first.. - */ -static inline unsigned long __ffs(unsigned long word) -{ - int k; - - k = 31; - if (word & 0x0000ffff) { k -= 16; word <<= 16; } - if (word & 0x00ff0000) { k -= 8; word <<= 8; } - if (word & 0x0f000000) { k -= 4; word <<= 4; } - if (word & 0x30000000) { k -= 2; word <<= 2; } - if (word & 0x40000000) { k -= 1; } - return k; -} - -/* - * fls: find last bit set. - */ - -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) - -/* - * ffs: find first bit set. This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ - -#define ffs(x) generic_ffs(x) +#include +#include +#include +#include #else @@ -381,37 +276,16 @@ static inline int constant_fls(int x) #define fls(x) \ ( __builtin_constant_p(x) ? constant_fls(x) : \ ({ int __r; asm("clz\t%0, %1" : "=r"(__r) : "r"(x) : "cc"); 32-__r; }) ) -#define fls64(x) generic_fls64(x) #define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); }) #define __ffs(x) (ffs(x) - 1) #define ffz(x) __ffs( ~(x) ) #endif -/* - * Find first bit set in a 168-bit bitmap, where the first - * 128 bits are unlikely to be set. - */ -static inline int sched_find_first_bit(const unsigned long *b) -{ - unsigned long v; - unsigned int off; - - for (off = 0; v = b[off], off < 4; off++) { - if (unlikely(v)) - break; - } - return __ffs(v) + off * 32; -} - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ +#include -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include +#include /* * Ext2 is defined to use little-endian byte ordering. @@ -426,7 +300,7 @@ static inline int sched_find_first_bit(const unsigned long *b) #define ext2_clear_bit_atomic(lock,nr,p) \ test_and_clear_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define ext2_test_bit(nr,p) \ - __test_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) + test_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define ext2_find_first_zero_bit(p,sz) \ _find_first_zero_bit_le(p,sz) #define ext2_find_next_zero_bit(p,sz,off) \ @@ -439,7 +313,7 @@ static inline int sched_find_first_bit(const unsigned long *b) #define minix_set_bit(nr,p) \ __set_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define minix_test_bit(nr,p) \ - __test_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) + test_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define minix_test_and_set_bit(nr,p) \ __test_and_set_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define minix_test_and_clear_bit(nr,p) \ -- cgit v1.2.3 From d142d8601993f423386556f1cbbacbdcf7b7a767 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:20 -0800 Subject: [PATCH] bitops: arm26: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove ffz() - remove __ffs() - remove generic_fls() - remove generic_fls64() - remove generic_ffs() - remove sched_find_first_bit() - remove generic_hweight{32,16,8}() Signed-off-by: Akinobu Mita Cc: Ian Molton Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm26/Kconfig | 4 ++ include/asm-arm26/bitops.h | 146 ++++----------------------------------------- 2 files changed, 14 insertions(+), 136 deletions(-) (limited to 'include') diff --git a/arch/arm26/Kconfig b/arch/arm26/Kconfig index dee23d87fc5a..cf4ebf4c274d 100644 --- a/arch/arm26/Kconfig +++ b/arch/arm26/Kconfig @@ -41,6 +41,10 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-arm26/bitops.h b/include/asm-arm26/bitops.h index d87f8634e625..19a69573a654 100644 --- a/include/asm-arm26/bitops.h +++ b/include/asm-arm26/bitops.h @@ -117,65 +117,7 @@ ____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p) return res & mask; } -/* - * Now the non-atomic variants. We let the compiler handle all - * optimisations for these. These are all _native_ endian. - */ -static inline void __set_bit(int nr, volatile unsigned long *p) -{ - p[nr >> 5] |= (1UL << (nr & 31)); -} - -static inline void __clear_bit(int nr, volatile unsigned long *p) -{ - p[nr >> 5] &= ~(1UL << (nr & 31)); -} - -static inline void __change_bit(int nr, volatile unsigned long *p) -{ - p[nr >> 5] ^= (1UL << (nr & 31)); -} - -static inline int __test_and_set_bit(int nr, volatile unsigned long *p) -{ - unsigned long oldval, mask = 1UL << (nr & 31); - - p += nr >> 5; - - oldval = *p; - *p = oldval | mask; - return oldval & mask; -} - -static inline int __test_and_clear_bit(int nr, volatile unsigned long *p) -{ - unsigned long oldval, mask = 1UL << (nr & 31); - - p += nr >> 5; - - oldval = *p; - *p = oldval & ~mask; - return oldval & mask; -} - -static inline int __test_and_change_bit(int nr, volatile unsigned long *p) -{ - unsigned long oldval, mask = 1UL << (nr & 31); - - p += nr >> 5; - - oldval = *p; - *p = oldval ^ mask; - return oldval & mask; -} - -/* - * This routine doesn't need to be atomic. - */ -static inline int __test_bit(int nr, const volatile unsigned long * p) -{ - return (p[nr >> 5] >> (nr & 31)) & 1UL; -} +#include /* * Little endian assembly bitops. nr = 0 -> byte 0 bit 0. @@ -211,7 +153,6 @@ extern int _find_next_bit_le(const unsigned long *p, int size, int offset); #define test_and_set_bit(nr,p) ATOMIC_BITOP_LE(test_and_set_bit,nr,p) #define test_and_clear_bit(nr,p) ATOMIC_BITOP_LE(test_and_clear_bit,nr,p) #define test_and_change_bit(nr,p) ATOMIC_BITOP_LE(test_and_change_bit,nr,p) -#define test_bit(nr,p) __test_bit(nr,p) #define find_first_zero_bit(p,sz) _find_first_zero_bit_le(p,sz) #define find_next_zero_bit(p,sz,off) _find_next_zero_bit_le(p,sz,off) #define find_first_bit(p,sz) _find_first_bit_le(p,sz) @@ -219,80 +160,13 @@ extern int _find_next_bit_le(const unsigned long *p, int size, int offset); #define WORD_BITOFF_TO_LE(x) ((x)) -/* - * ffz = Find First Zero in word. Undefined if no zero exists, - * so code should check against ~0UL first.. - */ -static inline unsigned long ffz(unsigned long word) -{ - int k; - - word = ~word; - k = 31; - if (word & 0x0000ffff) { k -= 16; word <<= 16; } - if (word & 0x00ff0000) { k -= 8; word <<= 8; } - if (word & 0x0f000000) { k -= 4; word <<= 4; } - if (word & 0x30000000) { k -= 2; word <<= 2; } - if (word & 0x40000000) { k -= 1; } - return k; -} - -/* - * ffz = Find First Zero in word. Undefined if no zero exists, - * so code should check against ~0UL first.. - */ -static inline unsigned long __ffs(unsigned long word) -{ - int k; - - k = 31; - if (word & 0x0000ffff) { k -= 16; word <<= 16; } - if (word & 0x00ff0000) { k -= 8; word <<= 8; } - if (word & 0x0f000000) { k -= 4; word <<= 4; } - if (word & 0x30000000) { k -= 2; word <<= 2; } - if (word & 0x40000000) { k -= 1; } - return k; -} - -/* - * fls: find last bit set. - */ - -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) - -/* - * ffs: find first bit set. This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ - -#define ffs(x) generic_ffs(x) - -/* - * Find first bit set in a 168-bit bitmap, where the first - * 128 bits are unlikely to be set. - */ -static inline int sched_find_first_bit(unsigned long *b) -{ - unsigned long v; - unsigned int off; - - for (off = 0; v = b[off], off < 4; off++) { - if (unlikely(v)) - break; - } - return __ffs(v) + off * 32; -} - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include +#include +#include +#include +#include +#include +#include /* * Ext2 is defined to use little-endian byte ordering. @@ -307,7 +181,7 @@ static inline int sched_find_first_bit(unsigned long *b) #define ext2_clear_bit_atomic(lock,nr,p) \ test_and_clear_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define ext2_test_bit(nr,p) \ - __test_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) + test_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define ext2_find_first_zero_bit(p,sz) \ _find_first_zero_bit_le(p,sz) #define ext2_find_next_zero_bit(p,sz,off) \ @@ -320,7 +194,7 @@ static inline int sched_find_first_bit(unsigned long *b) #define minix_set_bit(nr,p) \ __set_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define minix_test_bit(nr,p) \ - __test_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) + test_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define minix_test_and_set_bit(nr,p) \ __test_and_set_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define minix_test_and_clear_bit(nr,p) \ -- cgit v1.2.3 From e9f26df17266a8bd327957bd1a5957a263bd7112 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:21 -0800 Subject: [PATCH] bitops: cris: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove generic_fls() - remove generic_fls64() - remove generic_hweight{32,16,8}() - remove find_{next,first}{,_zero}_bit() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() - remove sched_find_first_bit() Signed-off-by: Akinobu Mita Acked-by: Mikael Starvik Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/cris/Kconfig | 8 ++ include/asm-cris/bitops.h | 234 ++-------------------------------------------- 2 files changed, 16 insertions(+), 226 deletions(-) (limited to 'include') diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index b83261949737..856b665020e7 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -16,6 +16,14 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-cris/bitops.h b/include/asm-cris/bitops.h index b1fca1fd8a5f..a569065113d9 100644 --- a/include/asm-cris/bitops.h +++ b/include/asm-cris/bitops.h @@ -39,8 +39,6 @@ struct __dummy { unsigned long a[100]; }; #define set_bit(nr, addr) (void)test_and_set_bit(nr, addr) -#define __set_bit(nr, addr) (void)__test_and_set_bit(nr, addr) - /* * clear_bit - Clears a bit in memory * @nr: Bit to clear @@ -54,8 +52,6 @@ struct __dummy { unsigned long a[100]; }; #define clear_bit(nr, addr) (void)test_and_clear_bit(nr, addr) -#define __clear_bit(nr, addr) (void)__test_and_clear_bit(nr, addr) - /* * change_bit - Toggle a bit in memory * @nr: Bit to change @@ -68,18 +64,6 @@ struct __dummy { unsigned long a[100]; }; #define change_bit(nr, addr) (void)test_and_change_bit(nr, addr) -/* - * __change_bit - Toggle a bit in memory - * @nr: the bit to change - * @addr: the address to start counting from - * - * Unlike change_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ - -#define __change_bit(nr, addr) (void)__test_and_change_bit(nr, addr) - /** * test_and_set_bit - Set a bit and return its old value * @nr: Bit to set @@ -104,18 +88,6 @@ static inline int test_and_set_bit(int nr, volatile unsigned long *addr) return retval; } -static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) -{ - unsigned int mask, retval; - unsigned int *adr = (unsigned int *)addr; - - adr += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *adr) != 0; - *adr |= mask; - return retval; -} - /* * clear_bit() doesn't provide any barrier for the compiler. */ @@ -146,27 +118,6 @@ static inline int test_and_clear_bit(int nr, volatile unsigned long *addr) return retval; } -/** - * __test_and_clear_bit - Clear a bit and return its old value - * @nr: Bit to clear - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ - -static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned int mask, retval; - unsigned int *adr = (unsigned int *)addr; - - adr += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *adr) != 0; - *adr &= ~mask; - return retval; -} /** * test_and_change_bit - Change a bit and return its old value * @nr: Bit to change @@ -190,42 +141,7 @@ static inline int test_and_change_bit(int nr, volatile unsigned long *addr) return retval; } -/* WARNING: non atomic and it can be reordered! */ - -static inline int __test_and_change_bit(int nr, volatile unsigned long *addr) -{ - unsigned int mask, retval; - unsigned int *adr = (unsigned int *)addr; - - adr += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *adr) != 0; - *adr ^= mask; - - return retval; -} - -/** - * test_bit - Determine whether a bit is set - * @nr: bit number to test - * @addr: Address to start counting from - * - * This routine doesn't need to be atomic. - */ - -static inline int test_bit(int nr, const volatile unsigned long *addr) -{ - unsigned int mask; - unsigned int *adr = (unsigned int *)addr; - - adr += nr >> 5; - mask = 1 << (nr & 0x1f); - return ((mask & *adr) != 0); -} - -/* - * Find-bit routines.. - */ +#include /* * Since we define it "external", it collides with the built-in @@ -234,152 +150,18 @@ static inline int test_bit(int nr, const volatile unsigned long *addr) */ #define ffs kernel_ffs -/* - * fls: find last bit set. - */ - -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) - -/* - * hweightN - returns the hamming weight of a N-bit word - * @x: the word to weigh - * - * The Hamming Weight of a number is the total number of bits set in it. - */ +#include +#include +#include +#include -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include -/** - * find_next_zero_bit - find the first zero bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static inline int find_next_zero_bit (const unsigned long * addr, int size, int offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - - found_first: - tmp |= ~0UL << size; - found_middle: - return result + ffz(tmp); -} - -/** - * find_next_bit - find the first set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static __inline__ int find_next_bit(const unsigned long *addr, int size, int offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp &= (~0UL << offset); - if (size < 32) - goto found_first; - if (tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if ((tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= (~0UL >> (32 - size)); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} - -/** - * find_first_zero_bit - find the first zero bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first zero bit, not the number of the byte - * containing a bit. - */ - -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - -#define ext2_set_bit __test_and_set_bit #define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) -#define ext2_clear_bit __test_and_clear_bit #define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) -#define ext2_test_bit test_bit -#define ext2_find_first_zero_bit find_first_zero_bit -#define ext2_find_next_zero_bit find_next_zero_bit - -/* Bitmap functions for the minix filesystem. */ -#define minix_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) -static inline int sched_find_first_bit(const unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (unlikely(b[3])) - return __ffs(b[3]) + 96; - if (b[4]) - return __ffs(b[4]) + 128; - return __ffs(b[5]) + 32 + 128; -} +#include +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From 1f6d7a93c141a473e2cff428a3dbf13b4bec9325 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:22 -0800 Subject: [PATCH] bitops: frv: use generic bitops - remove ffz() - remove find_{next,first}{,_zero}_bit() - remove generic_ffs() - remove __ffs() - remove generic_fls64() - remove sched_find_first_bit() - remove generic_hweight{32,16,8}() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/frv/Kconfig | 4 ++ include/asm-frv/bitops.h | 170 +++-------------------------------------------- 2 files changed, 13 insertions(+), 161 deletions(-) (limited to 'include') diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index e08383712370..95a3892b8d1b 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -17,6 +17,10 @@ config GENERIC_FIND_NEXT_BIT bool default y +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default n diff --git a/include/asm-frv/bitops.h b/include/asm-frv/bitops.h index 9c5db5c34c1b..6344d06390b9 100644 --- a/include/asm-frv/bitops.h +++ b/include/asm-frv/bitops.h @@ -22,20 +22,7 @@ #ifdef __KERNEL__ -/* - * ffz = Find First Zero in word. Undefined if no zero exists, - * so code should check against ~0UL first.. - */ -static inline unsigned long ffz(unsigned long word) -{ - unsigned long result = 0; - - while (word & 1) { - result++; - word >>= 1; - } - return result; -} +#include /* * clear_bit() doesn't provide any barrier for the compiler. @@ -171,51 +158,9 @@ static inline int __test_bit(int nr, const volatile void * addr) __constant_test_bit((nr),(addr)) : \ __test_bit((nr),(addr))) -extern int find_next_bit(const unsigned long *addr, int size, int offset); - -#define find_first_bit(addr, size) find_next_bit(addr, size, 0) - -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) - -static inline int find_next_zero_bit(const void *addr, int size, int offset) -{ - const unsigned long *p = ((const unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; -found_middle: - return result + ffz(tmp); -} - -#define ffs(x) generic_ffs(x) -#define __ffs(x) (ffs(x) - 1) +#include +#include +#include /* * fls: find last bit set. @@ -228,114 +173,17 @@ found_middle: \ bit ? 33 - bit : bit; \ }) -#define fls64(x) generic_fls64(x) -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(const unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} +#include +#include +#include - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -#define ext2_set_bit(nr, addr) __test_and_set_bit ((nr) ^ 0x18, (addr)) -#define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr) ^ 0x18, (addr)) +#include #define ext2_set_bit_atomic(lock,nr,addr) test_and_set_bit ((nr) ^ 0x18, (addr)) #define ext2_clear_bit_atomic(lock,nr,addr) test_and_clear_bit((nr) ^ 0x18, (addr)) -static inline int ext2_test_bit(int nr, const volatile void * addr) -{ - const volatile unsigned char *ADDR = (const unsigned char *) addr; - int mask; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - return ((mask & *ADDR) != 0); -} - -#define ext2_find_first_zero_bit(addr, size) \ - ext2_find_next_zero_bit((addr), (size), 0) - -static inline unsigned long ext2_find_next_zero_bit(const void *addr, - unsigned long size, - unsigned long offset) -{ - const unsigned long *p = ((const unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if(offset) { - /* We hold the little endian value in tmp, but then the - * shift is illegal. So we could keep a big endian value - * in tmp, like this: - * - * tmp = __swab32(*(p++)); - * tmp |= ~0UL >> (32-offset); - * - * but this would decrease preformance, so we change the - * shift: - */ - tmp = *(p++); - tmp |= __swab32(~0UL >> (32-offset)); - if(size < 32) - goto found_first; - if(~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while(size & ~31UL) { - if(~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if(!size) - return result; - tmp = *p; - -found_first: - /* tmp is little endian, so we would have to swab the shift, - * see above. But then we have to swab tmp below for ffz, so - * we might as well do this here. - */ - return result + ffz(__swab32(tmp) | (~0UL << size)); -found_middle: - return result + ffz(__swab32(tmp)); -} - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit ((nr) ^ 0x18, (addr)) -#define minix_set_bit(nr,addr) __set_bit((nr) ^ 0x18, (addr)) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit((nr) ^ 0x18, (addr)) -#define minix_test_bit(nr,addr) ext2_test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) ext2_find_first_zero_bit(addr,size) +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From f6e0213f7c253086e17844df8ba21075100b5ead Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:23 -0800 Subject: [PATCH] bitops: h8300: use generic bitops - remove generic_ffs() - remove find_{next,first}{,_zero}_bit() - remove sched_find_first_bit() - remove generic_hweight{32,16,8}() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove ext2_{set,clear}_bit_atomic() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() - remove generic_fls() - remove generic_fls64() Signed-off-by: Akinobu Mita Cc: Yoshinori Sato Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/h8300/Kconfig | 8 ++ include/asm-h8300/bitops.h | 222 ++------------------------------------------- 2 files changed, 17 insertions(+), 213 deletions(-) (limited to 'include') diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 98308b018a35..cabf0bfffc53 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -29,6 +29,14 @@ config RWSEM_XCHGADD_ALGORITHM bool default n +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-h8300/bitops.h b/include/asm-h8300/bitops.h index af95f914e51c..574f57b6c4d1 100644 --- a/include/asm-h8300/bitops.h +++ b/include/asm-h8300/bitops.h @@ -8,7 +8,6 @@ #include #include -#include /* swab32 */ #include #ifdef __KERNEL__ @@ -177,10 +176,7 @@ H8300_GEN_TEST_BITOP(test_and_change_bit,"bnot") #undef H8300_GEN_TEST_BITOP_CONST_INT #undef H8300_GEN_TEST_BITOP -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) - -#define ffs(x) generic_ffs(x) +#include static __inline__ unsigned long __ffs(unsigned long word) { @@ -196,216 +192,16 @@ static __inline__ unsigned long __ffs(unsigned long word) return result; } -static __inline__ int find_next_zero_bit (const unsigned long * addr, int size, int offset) -{ - unsigned long *p = (unsigned long *)(((unsigned long)addr + (offset >> 3)) & ~3); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; -found_middle: - return result + ffz(tmp); -} - -static __inline__ unsigned long find_next_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - unsigned long *p = (unsigned long *)(((unsigned long)addr + (offset >> 3)) & ~3); - unsigned int result = offset & ~31UL; - unsigned int tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp &= ~0UL << offset; - if (size < 32) - goto found_first; - if (tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size >= 32) { - if ((tmp = *p++) != 0) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= ~0UL >> (32 - size); - if (tmp == 0UL) - return result + size; -found_middle: - return result + __ffs(tmp); -} - -#define find_first_bit(addr, size) find_next_bit(addr, size, 0) - -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -static __inline__ int ext2_set_bit(int nr, volatile void * addr) -{ - int mask, retval; - unsigned long flags; - volatile unsigned char *ADDR = (unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - local_irq_save(flags); - retval = (mask & *ADDR) != 0; - *ADDR |= mask; - local_irq_restore(flags); - return retval; -} -#define ext2_set_bit_atomic(lock, nr, addr) ext2_set_bit(nr, addr) - -static __inline__ int ext2_clear_bit(int nr, volatile void * addr) -{ - int mask, retval; - unsigned long flags; - volatile unsigned char *ADDR = (unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - local_irq_save(flags); - retval = (mask & *ADDR) != 0; - *ADDR &= ~mask; - local_irq_restore(flags); - return retval; -} -#define ext2_clear_bit_atomic(lock, nr, addr) ext2_set_bit(nr, addr) - -static __inline__ int ext2_test_bit(int nr, const volatile void * addr) -{ - int mask; - const volatile unsigned char *ADDR = (const unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - return ((mask & *ADDR) != 0); -} - -#define ext2_find_first_zero_bit(addr, size) \ - ext2_find_next_zero_bit((addr), (size), 0) - -static __inline__ unsigned long ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if(offset) { - /* We hold the little endian value in tmp, but then the - * shift is illegal. So we could keep a big endian value - * in tmp, like this: - * - * tmp = __swab32(*(p++)); - * tmp |= ~0UL >> (32-offset); - * - * but this would decrease performance, so we change the - * shift: - */ - tmp = *(p++); - tmp |= __swab32(~0UL >> (32-offset)); - if(size < 32) - goto found_first; - if(~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while(size & ~31UL) { - if(~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if(!size) - return result; - tmp = *p; - -found_first: - /* tmp is little endian, so we would have to swab the shift, - * see above. But then we have to swab tmp below for ffz, so - * we might as well do this here. - */ - return result + ffz(__swab32(tmp) | (~0UL << size)); -found_middle: - return result + ffz(__swab32(tmp)); -} - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) __set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) +#include +#include +#include +#include +#include +#include #endif /* __KERNEL__ */ -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) +#include +#include #endif /* _H8300_BITOPS_H */ -- cgit v1.2.3 From 1cc2b9943b7b3a8d526aa8be5450d3ec083c3de4 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:24 -0800 Subject: [PATCH] bitops: i386: use generic bitops - remove generic_fls64() - remove sched_find_first_bit() - remove generic_hweight{32,16,8}() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/Kconfig | 4 ++++ include/asm-i386/bitops.h | 55 +++++++---------------------------------------- 2 files changed, 12 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index b008fb0cd7b7..f7db71d0b913 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -37,6 +37,10 @@ config GENERIC_IOMAP bool default y +config GENERIC_HWEIGHT + bool + default y + config ARCH_MAY_HAVE_PC_FDC bool default y diff --git a/include/asm-i386/bitops.h b/include/asm-i386/bitops.h index 7d20b95edb3b..08deaeee6be9 100644 --- a/include/asm-i386/bitops.h +++ b/include/asm-i386/bitops.h @@ -362,28 +362,9 @@ static inline unsigned long ffz(unsigned long word) return word; } -#define fls64(x) generic_fls64(x) - #ifdef __KERNEL__ -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(const unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} +#include /** * ffs - find first bit set @@ -421,42 +402,22 @@ static inline int fls(int x) return r+1; } -/** - * hweightN - returns the hamming weight of a N-bit word - * @x: the word to weigh - * - * The Hamming Weight of a number is the total number of bits set in it. - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include #endif /* __KERNEL__ */ +#include + #ifdef __KERNEL__ -#define ext2_set_bit(nr,addr) \ - __test_and_set_bit((nr),(unsigned long*)addr) +#include + #define ext2_set_bit_atomic(lock,nr,addr) \ test_and_set_bit((nr),(unsigned long*)addr) -#define ext2_clear_bit(nr, addr) \ - __test_and_clear_bit((nr),(unsigned long*)addr) #define ext2_clear_bit_atomic(lock,nr, addr) \ test_and_clear_bit((nr),(unsigned long*)addr) -#define ext2_test_bit(nr, addr) test_bit((nr),(unsigned long*)addr) -#define ext2_find_first_zero_bit(addr, size) \ - find_first_zero_bit((unsigned long*)addr, size) -#define ext2_find_next_zero_bit(addr, size, off) \ - find_next_zero_bit((unsigned long*)addr, size, off) - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,(void*)addr) -#define minix_set_bit(nr,addr) __set_bit(nr,(void*)addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,(void*)addr) -#define minix_test_bit(nr,addr) test_bit(nr,(void*)addr) -#define minix_find_first_zero_bit(addr,size) \ - find_first_zero_bit((void*)addr,size) + +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From 2875aef8bd0e42367a66a78ef7abe10f3bba27b5 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:25 -0800 Subject: [PATCH] bitops: ia64: use generic bitops - remove generic_fls64() - remove find_{next,first}{,_zero}_bit() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() - remove sched_find_first_bit() Signed-off-by: Akinobu Mita Cc: "Luck, Tony" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/Kconfig | 4 +++ arch/ia64/lib/Makefile | 2 +- arch/ia64/lib/bitop.c | 88 ----------------------------------------------- include/asm-ia64/bitops.h | 67 +++++++++--------------------------- 4 files changed, 22 insertions(+), 139 deletions(-) delete mode 100644 arch/ia64/lib/bitop.c (limited to 'include') diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index d790a6d90261..edffe25a477a 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -34,6 +34,10 @@ config RWSEM_XCHGADD_ALGORITHM bool default y +config GENERIC_FIND_NEXT_BIT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/arch/ia64/lib/Makefile b/arch/ia64/lib/Makefile index ac64664a1807..d8536a2c22a9 100644 --- a/arch/ia64/lib/Makefile +++ b/arch/ia64/lib/Makefile @@ -6,7 +6,7 @@ obj-y := io.o lib-y := __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \ __divdi3.o __udivdi3.o __moddi3.o __umoddi3.o \ - bitop.o checksum.o clear_page.o csum_partial_copy.o \ + checksum.o clear_page.o csum_partial_copy.o \ clear_user.o strncpy_from_user.o strlen_user.o strnlen_user.o \ flush.o ip_fast_csum.o do_csum.o \ memset.o strlen.o diff --git a/arch/ia64/lib/bitop.c b/arch/ia64/lib/bitop.c deleted file mode 100644 index 82e299c8464e..000000000000 --- a/arch/ia64/lib/bitop.c +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include -#include -#include -#include - -/* - * Find next zero bit in a bitmap reasonably efficiently.. - */ - -int __find_next_zero_bit (const void *addr, unsigned long size, unsigned long offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 6); - unsigned long result = offset & ~63UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 63UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (64-offset); - if (size < 64) - goto found_first; - if (~tmp) - goto found_middle; - size -= 64; - result += 64; - } - while (size & ~63UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 64; - size -= 64; - } - if (!size) - return result; - tmp = *p; -found_first: - tmp |= ~0UL << size; - if (tmp == ~0UL) /* any bits zero? */ - return result + size; /* nope */ -found_middle: - return result + ffz(tmp); -} -EXPORT_SYMBOL(__find_next_zero_bit); - -/* - * Find next bit in a bitmap reasonably efficiently.. - */ -int __find_next_bit(const void *addr, unsigned long size, unsigned long offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 6); - unsigned long result = offset & ~63UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 63UL; - if (offset) { - tmp = *(p++); - tmp &= ~0UL << offset; - if (size < 64) - goto found_first; - if (tmp) - goto found_middle; - size -= 64; - result += 64; - } - while (size & ~63UL) { - if ((tmp = *(p++))) - goto found_middle; - result += 64; - size -= 64; - } - if (!size) - return result; - tmp = *p; - found_first: - tmp &= ~0UL >> (64-size); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ - found_middle: - return result + __ffs(tmp); -} -EXPORT_SYMBOL(__find_next_bit); diff --git a/include/asm-ia64/bitops.h b/include/asm-ia64/bitops.h index eccb01c79c1a..90921e162793 100644 --- a/include/asm-ia64/bitops.h +++ b/include/asm-ia64/bitops.h @@ -5,8 +5,8 @@ * Copyright (C) 1998-2003 Hewlett-Packard Co * David Mosberger-Tang * - * 02/06/02 find_next_bit() and find_first_bit() added from Erich Focht's ia64 O(1) - * scheduler patch + * 02/06/02 find_next_bit() and find_first_bit() added from Erich Focht's ia64 + * O(1) scheduler patch */ #include @@ -25,9 +25,9 @@ * restricted to acting on a single-word quantity. * * The address must be (at least) "long" aligned. - * Note that there are driver (e.g., eepro100) which use these operations to operate on - * hw-defined data-structures, so we can't easily change these operations to force a - * bigger alignment. + * Note that there are driver (e.g., eepro100) which use these operations to + * operate on hw-defined data-structures, so we can't easily change these + * operations to force a bigger alignment. * * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). */ @@ -284,8 +284,8 @@ test_bit (int nr, const volatile void *addr) * ffz - find the first zero bit in a long word * @x: The long word to find the bit in * - * Returns the bit-number (0..63) of the first (least significant) zero bit. Undefined if - * no zero exists, so code should check against ~0UL first... + * Returns the bit-number (0..63) of the first (least significant) zero bit. + * Undefined if no zero exists, so code should check against ~0UL first... */ static inline unsigned long ffz (unsigned long x) @@ -345,13 +345,14 @@ fls (int t) x |= x >> 16; return ia64_popcnt(x); } -#define fls64(x) generic_fls64(x) + +#include /* - * ffs: find first bit set. This is defined the same way as the libc and compiler builtin - * ffs routines, therefore differs in spirit from the above ffz (man ffs): it operates on - * "int" values only and the result value is the bit number + 1. ffs(0) is defined to - * return zero. + * ffs: find first bit set. This is defined the same way as the libc and + * compiler builtin ffs routines, therefore differs in spirit from the above + * ffz (man ffs): it operates on "int" values only and the result value is the + * bit number + 1. ffs(0) is defined to return zero. */ #define ffs(x) __builtin_ffs(x) @@ -373,51 +374,17 @@ hweight64 (unsigned long x) #endif /* __KERNEL__ */ -extern int __find_next_zero_bit (const void *addr, unsigned long size, - unsigned long offset); -extern int __find_next_bit(const void *addr, unsigned long size, - unsigned long offset); - -#define find_next_zero_bit(addr, size, offset) \ - __find_next_zero_bit((addr), (size), (offset)) -#define find_next_bit(addr, size, offset) \ - __find_next_bit((addr), (size), (offset)) - -/* - * The optimizer actually does good code for this case.. - */ -#define find_first_zero_bit(addr, size) find_next_zero_bit((addr), (size), 0) - -#define find_first_bit(addr, size) find_next_bit((addr), (size), 0) +#include #ifdef __KERNEL__ -#define __clear_bit(nr, addr) clear_bit(nr, addr) +#include -#define ext2_set_bit __test_and_set_bit #define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) -#define ext2_clear_bit __test_and_clear_bit #define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) -#define ext2_test_bit test_bit -#define ext2_find_first_zero_bit find_first_zero_bit -#define ext2_find_next_zero_bit find_next_zero_bit - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) __set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) -static inline int -sched_find_first_bit (unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return 64 + __ffs(b[1]); - return __ffs(b[2]) + 128; -} +#include +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From 6d9f937b559d664b6f222cb91eca9c6802bfe89a Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:26 -0800 Subject: [PATCH] bitops: m32r: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove ffz() - remove find_{next,first}{,_zero}_bit() - remove __ffs() - remove generic_fls() - remove generic_fls64() - remove sched_find_first_bit() - remove generic_ffs() - remove generic_hweight{32,16,8}() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove ext2_{set,clear}_bit_atomic() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: Hirokazu Takata Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/m32r/Kconfig | 8 + include/asm-m32r/bitops.h | 457 ++-------------------------------------------- 2 files changed, 20 insertions(+), 445 deletions(-) (limited to 'include') diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index a3dcc3fab4b7..05c864c6c2d9 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -214,6 +214,14 @@ config RWSEM_XCHGADD_ALGORITHM bool default n +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-m32r/bitops.h b/include/asm-m32r/bitops.h index f8e993e0bbc0..902a366101a5 100644 --- a/include/asm-m32r/bitops.h +++ b/include/asm-m32r/bitops.h @@ -62,25 +62,6 @@ static __inline__ void set_bit(int nr, volatile void * addr) local_irq_restore(flags); } -/** - * __set_bit - Set a bit in memory - * @nr: the bit to set - * @addr: the address to start counting from - * - * Unlike set_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static __inline__ void __set_bit(int nr, volatile void * addr) -{ - __u32 mask; - volatile __u32 *a = addr; - - a += (nr >> 5); - mask = (1 << (nr & 0x1F)); - *a |= mask; -} - /** * clear_bit - Clears a bit in memory * @nr: Bit to clear @@ -118,38 +99,9 @@ static __inline__ void clear_bit(int nr, volatile void * addr) local_irq_restore(flags); } -static __inline__ void __clear_bit(int nr, volatile unsigned long * addr) -{ - unsigned long mask; - volatile unsigned long *a = addr; - - a += (nr >> 5); - mask = (1 << (nr & 0x1F)); - *a &= ~mask; -} - #define smp_mb__before_clear_bit() barrier() #define smp_mb__after_clear_bit() barrier() -/** - * __change_bit - Toggle a bit in memory - * @nr: the bit to set - * @addr: the address to start counting from - * - * Unlike change_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static __inline__ void __change_bit(int nr, volatile void * addr) -{ - __u32 mask; - volatile __u32 *a = addr; - - a += (nr >> 5); - mask = (1 << (nr & 0x1F)); - *a ^= mask; -} - /** * change_bit - Toggle a bit in memory * @nr: Bit to clear @@ -220,28 +172,6 @@ static __inline__ int test_and_set_bit(int nr, volatile void * addr) return (oldbit != 0); } -/** - * __test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ -static __inline__ int __test_and_set_bit(int nr, volatile void * addr) -{ - __u32 mask, oldbit; - volatile __u32 *a = addr; - - a += (nr >> 5); - mask = (1 << (nr & 0x1F)); - oldbit = (*a & mask); - *a |= mask; - - return (oldbit != 0); -} - /** * test_and_clear_bit - Clear a bit and return its old value * @nr: Bit to set @@ -279,42 +209,6 @@ static __inline__ int test_and_clear_bit(int nr, volatile void * addr) return (oldbit != 0); } -/** - * __test_and_clear_bit - Clear a bit and return its old value - * @nr: Bit to set - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ -static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) -{ - __u32 mask, oldbit; - volatile __u32 *a = addr; - - a += (nr >> 5); - mask = (1 << (nr & 0x1F)); - oldbit = (*a & mask); - *a &= ~mask; - - return (oldbit != 0); -} - -/* WARNING: non atomic and it can be reordered! */ -static __inline__ int __test_and_change_bit(int nr, volatile void * addr) -{ - __u32 mask, oldbit; - volatile __u32 *a = addr; - - a += (nr >> 5); - mask = (1 << (nr & 0x1F)); - oldbit = (*a & mask); - *a ^= mask; - - return (oldbit != 0); -} - /** * test_and_change_bit - Change a bit and return its old value * @nr: Bit to set @@ -350,353 +244,26 @@ static __inline__ int test_and_change_bit(int nr, volatile void * addr) return (oldbit != 0); } -/** - * test_bit - Determine whether a bit is set - * @nr: bit number to test - * @addr: Address to start counting from - */ -static __inline__ int test_bit(int nr, const volatile void * addr) -{ - __u32 mask; - const volatile __u32 *a = addr; - - a += (nr >> 5); - mask = (1 << (nr & 0x1F)); - - return ((*a & mask) != 0); -} - -/** - * ffz - find first zero in word. - * @word: The word to search - * - * Undefined if no zero exists, so code should check against ~0UL first. - */ -static __inline__ unsigned long ffz(unsigned long word) -{ - int k; - - word = ~word; - k = 0; - if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } - if (!(word & 0x000000ff)) { k += 8; word >>= 8; } - if (!(word & 0x0000000f)) { k += 4; word >>= 4; } - if (!(word & 0x00000003)) { k += 2; word >>= 2; } - if (!(word & 0x00000001)) { k += 1; } - - return k; -} - -/** - * find_first_zero_bit - find the first zero bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first zero bit, not the number of the byte - * containing a bit. - */ - -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) - -/** - * find_next_zero_bit - find the first zero bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static __inline__ int find_next_zero_bit(const unsigned long *addr, - int size, int offset) -{ - const unsigned long *p = addr + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; -found_middle: - return result + ffz(tmp); -} - -/** - * __ffs - find first bit in word. - * @word: The word to search - * - * Undefined if no bit exists, so code should check against 0 first. - */ -static __inline__ unsigned long __ffs(unsigned long word) -{ - int k = 0; - - if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } - if (!(word & 0x000000ff)) { k += 8; word >>= 8; } - if (!(word & 0x0000000f)) { k += 4; word >>= 4; } - if (!(word & 0x00000003)) { k += 2; word >>= 2; } - if (!(word & 0x00000001)) { k += 1;} - - return k; -} - -/* - * fls: find last bit set. - */ -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) +#include +#include +#include +#include +#include #ifdef __KERNEL__ -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} - -/** - * find_next_bit - find the first set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static inline unsigned long find_next_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - unsigned int *p = ((unsigned int *) addr) + (offset >> 5); - unsigned int result = offset & ~31UL; - unsigned int tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *p++; - tmp &= ~0UL << offset; - if (size < 32) - goto found_first; - if (tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size >= 32) { - if ((tmp = *p++) != 0) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= ~0UL >> (32 - size); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} - -/** - * find_first_bit - find the first set bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first set bit, not the number of the byte - * containing a bit. - */ -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - -/** - * ffs - find first bit set - * @x: the word to search - * - * This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ -#define ffs(x) generic_ffs(x) - -/** - * hweightN - returns the hamming weight of a N-bit word - * @x: the word to weigh - * - * The Hamming Weight of a number is the total number of bits set in it. - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include +#include +#include +#include #endif /* __KERNEL__ */ #ifdef __KERNEL__ -/* - * ext2_XXXX function - * orig: include/asm-sh/bitops.h - */ - -#ifdef __LITTLE_ENDIAN__ -#define ext2_set_bit __test_and_set_bit -#define ext2_clear_bit __test_and_clear_bit -#define ext2_test_bit test_bit -#define ext2_find_first_zero_bit find_first_zero_bit -#define ext2_find_next_zero_bit find_next_zero_bit -#else -static inline int ext2_set_bit(int nr, volatile void * addr) -{ - __u8 mask, oldbit; - volatile __u8 *a = addr; - - a += (nr >> 3); - mask = (1 << (nr & 0x07)); - oldbit = (*a & mask); - *a |= mask; - - return (oldbit != 0); -} - -static inline int ext2_clear_bit(int nr, volatile void * addr) -{ - __u8 mask, oldbit; - volatile __u8 *a = addr; - - a += (nr >> 3); - mask = (1 << (nr & 0x07)); - oldbit = (*a & mask); - *a &= ~mask; - - return (oldbit != 0); -} - -static inline int ext2_test_bit(int nr, const volatile void * addr) -{ - __u32 mask; - const volatile __u8 *a = addr; - - a += (nr >> 3); - mask = (1 << (nr & 0x07)); - - return ((mask & *a) != 0); -} - -#define ext2_find_first_zero_bit(addr, size) \ - ext2_find_next_zero_bit((addr), (size), 0) - -static inline unsigned long ext2_find_next_zero_bit(void *addr, - unsigned long size, unsigned long offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if(offset) { - /* We hold the little endian value in tmp, but then the - * shift is illegal. So we could keep a big endian value - * in tmp, like this: - * - * tmp = __swab32(*(p++)); - * tmp |= ~0UL >> (32-offset); - * - * but this would decrease preformance, so we change the - * shift: - */ - tmp = *(p++); - tmp |= __swab32(~0UL >> (32-offset)); - if(size < 32) - goto found_first; - if(~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while(size & ~31UL) { - if(~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if(!size) - return result; - tmp = *p; - -found_first: - /* tmp is little endian, so we would have to swab the shift, - * see above. But then we have to swab tmp below for ffz, so - * we might as well do this here. - */ - return result + ffz(__swab32(tmp) | (~0UL << size)); -found_middle: - return result + ffz(__swab32(tmp)); -} -#endif - -#define ext2_set_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_set_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -#define ext2_clear_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_clear_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) __set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) +#include +#include +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From ba1a5b32ba5e2fcb53b6943b8c9c33694ac68ce5 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:27 -0800 Subject: [PATCH] bitops: m68k: use generic bitops - remove generic_fls64() - remove sched_find_first_bit() - remove generic_hweight() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() Signed-off-by: Akinobu Mita Cc: Roman Zippel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/m68k/Kconfig | 4 +++ include/asm-m68k/bitops.h | 86 +++-------------------------------------------- 2 files changed, 9 insertions(+), 81 deletions(-) (limited to 'include') diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 8849439e88dd..805b81fedf80 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -17,6 +17,10 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-m68k/bitops.h b/include/asm-m68k/bitops.h index b7955b39d963..e845daac48ad 100644 --- a/include/asm-m68k/bitops.h +++ b/include/asm-m68k/bitops.h @@ -310,36 +310,10 @@ static inline int fls(int x) return 32 - cnt; } -#define fls64(x) generic_fls64(x) - -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(const unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include +#include +#include /* Bitmap functions for the minix filesystem */ @@ -377,61 +351,11 @@ static inline int minix_test_bit(int nr, const void *vaddr) /* Bitmap functions for the ext2 filesystem. */ -#define ext2_set_bit(nr, addr) __test_and_set_bit((nr) ^ 24, (unsigned long *)(addr)) +#include + #define ext2_set_bit_atomic(lock, nr, addr) test_and_set_bit((nr) ^ 24, (unsigned long *)(addr)) -#define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr) ^ 24, (unsigned long *)(addr)) #define ext2_clear_bit_atomic(lock, nr, addr) test_and_clear_bit((nr) ^ 24, (unsigned long *)(addr)) -static inline int ext2_test_bit(int nr, const void *vaddr) -{ - const unsigned char *p = vaddr; - return (p[nr >> 3] & (1U << (nr & 7))) != 0; -} - -static inline int ext2_find_first_zero_bit(const void *vaddr, unsigned size) -{ - const unsigned long *p = vaddr, *addr = vaddr; - int res; - - if (!size) - return 0; - - size = (size >> 5) + ((size & 31) > 0); - while (*p++ == ~0UL) - { - if (--size == 0) - return (p - addr) << 5; - } - - --p; - for (res = 0; res < 32; res++) - if (!ext2_test_bit (res, p)) - break; - return (p - addr) * 32 + res; -} - -static inline int ext2_find_next_zero_bit(const void *vaddr, unsigned size, - unsigned offset) -{ - const unsigned long *addr = vaddr; - const unsigned long *p = addr + (offset >> 5); - int bit = offset & 31UL, res; - - if (offset >= size) - return size; - - if (bit) { - /* Look for zero in first longword */ - for (res = bit; res < 32; res++) - if (!ext2_test_bit (res, p)) - return (p - addr) * 32 + res; - p++; - } - /* No zero yet, search remaining full bytes for a zero */ - res = ext2_find_first_zero_bit (p, size - 32 * (p - addr)); - return (p - addr) * 32 + res; -} - #endif /* __KERNEL__ */ #endif /* _M68K_BITOPS_H */ -- cgit v1.2.3 From d5728b45da6a510b74f07e385b3ef6b434b828e5 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:28 -0800 Subject: [PATCH] m68k: fix undefined reference to generic_find_next_zero_le_bit This patch reverts ext2 bitmap functions. Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-m68k/bitops.h | 54 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-m68k/bitops.h b/include/asm-m68k/bitops.h index e845daac48ad..1a61fdb56aaf 100644 --- a/include/asm-m68k/bitops.h +++ b/include/asm-m68k/bitops.h @@ -351,11 +351,61 @@ static inline int minix_test_bit(int nr, const void *vaddr) /* Bitmap functions for the ext2 filesystem. */ -#include - +#define ext2_set_bit(nr, addr) __test_and_set_bit((nr) ^ 24, (unsigned long *)(addr)) #define ext2_set_bit_atomic(lock, nr, addr) test_and_set_bit((nr) ^ 24, (unsigned long *)(addr)) +#define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr) ^ 24, (unsigned long *)(addr)) #define ext2_clear_bit_atomic(lock, nr, addr) test_and_clear_bit((nr) ^ 24, (unsigned long *)(addr)) +static inline int ext2_test_bit(int nr, const void *vaddr) +{ + const unsigned char *p = vaddr; + return (p[nr >> 3] & (1U << (nr & 7))) != 0; +} + +static inline int ext2_find_first_zero_bit(const void *vaddr, unsigned size) +{ + const unsigned long *p = vaddr, *addr = vaddr; + int res; + + if (!size) + return 0; + + size = (size >> 5) + ((size & 31) > 0); + while (*p++ == ~0UL) + { + if (--size == 0) + return (p - addr) << 5; + } + + --p; + for (res = 0; res < 32; res++) + if (!ext2_test_bit (res, p)) + break; + return (p - addr) * 32 + res; +} + +static inline int ext2_find_next_zero_bit(const void *vaddr, unsigned size, + unsigned offset) +{ + const unsigned long *addr = vaddr; + const unsigned long *p = addr + (offset >> 5); + int bit = offset & 31UL, res; + + if (offset >= size) + return size; + + if (bit) { + /* Look for zero in first longword */ + for (res = bit; res < 32; res++) + if (!ext2_test_bit (res, p)) + return (p - addr) * 32 + res; + p++; + } + /* No zero yet, search remaining full bytes for a zero */ + res = ext2_find_first_zero_bit (p, size - 32 * (p - addr)); + return (p - addr) * 32 + res; +} + #endif /* __KERNEL__ */ #endif /* _M68K_BITOPS_H */ -- cgit v1.2.3 From d2d7cdcf6e6d2a3db9885316d075940f12324413 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:29 -0800 Subject: [PATCH] bitops: m68knommu: use generic bitops - remove ffs() - remove __ffs() - remove sched_find_first_bit() - remove ffz() - remove find_{next,first}{,_zero}_bit() - remove generic_hweight() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() - remove generic_fls() - remove generic_fls64() Signed-off-by: Akinobu Mita Cc: Greg Ungerer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/m68knommu/Kconfig | 8 ++ include/asm-m68knommu/bitops.h | 221 ++--------------------------------------- 2 files changed, 17 insertions(+), 212 deletions(-) (limited to 'include') diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig index e50858dbc237..3cde6822ead1 100644 --- a/arch/m68knommu/Kconfig +++ b/arch/m68knommu/Kconfig @@ -25,6 +25,14 @@ config RWSEM_XCHGADD_ALGORITHM bool default n +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-m68knommu/bitops.h b/include/asm-m68knommu/bitops.h index 00c4a2548e60..0b68ccd327f7 100644 --- a/include/asm-m68knommu/bitops.h +++ b/include/asm-m68knommu/bitops.h @@ -12,104 +12,10 @@ #ifdef __KERNEL__ -/* - * Generic ffs(). - */ -static inline int ffs(int x) -{ - int r = 1; - - if (!x) - return 0; - if (!(x & 0xffff)) { - x >>= 16; - r += 16; - } - if (!(x & 0xff)) { - x >>= 8; - r += 8; - } - if (!(x & 0xf)) { - x >>= 4; - r += 4; - } - if (!(x & 3)) { - x >>= 2; - r += 2; - } - if (!(x & 1)) { - x >>= 1; - r += 1; - } - return r; -} - -/* - * Generic __ffs(). - */ -static inline int __ffs(int x) -{ - int r = 0; - - if (!x) - return 0; - if (!(x & 0xffff)) { - x >>= 16; - r += 16; - } - if (!(x & 0xff)) { - x >>= 8; - r += 8; - } - if (!(x & 0xf)) { - x >>= 4; - r += 4; - } - if (!(x & 3)) { - x >>= 2; - r += 2; - } - if (!(x & 1)) { - x >>= 1; - r += 1; - } - return r; -} - -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} - -/* - * ffz = Find First Zero in word. Undefined if no zero exists, - * so code should check against ~0UL first.. - */ -static __inline__ unsigned long ffz(unsigned long word) -{ - unsigned long result = 0; - - while(word & 1) { - result++; - word >>= 1; - } - return result; -} - +#include +#include +#include +#include static __inline__ void set_bit(int nr, volatile unsigned long * addr) { @@ -254,98 +160,8 @@ static __inline__ int __test_bit(int nr, const volatile unsigned long * addr) __constant_test_bit((nr),(addr)) : \ __test_bit((nr),(addr))) -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - -static __inline__ int find_next_zero_bit (const void * addr, int size, int offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; -found_middle: - return result + ffz(tmp); -} - -/* - * Find next one bit in a bitmap reasonably efficiently. - */ -static __inline__ unsigned long find_next_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - unsigned int *p = ((unsigned int *) addr) + (offset >> 5); - unsigned int result = offset & ~31UL; - unsigned int tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *p++; - tmp &= ~0UL << offset; - if (size < 32) - goto found_first; - if (tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size >= 32) { - if ((tmp = *p++) != 0) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= ~0UL >> (32 - size); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - +#include +#include static __inline__ int ext2_set_bit(int nr, volatile void * addr) { @@ -475,30 +291,11 @@ found_middle: return result + ffz(__swab32(tmp)); } -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) __set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) - -/** - * hweightN - returns the hamming weight of a N-bit word - * @x: the word to weigh - * - * The Hamming Weight of a number is the total number of bits set in it. - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include #endif /* __KERNEL__ */ -/* - * fls: find last bit set. - */ -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) +#include +#include #endif /* _M68KNOMMU_BITOPS_H */ -- cgit v1.2.3 From 3c9ee7ef87414cba80dbdf433d3547bb20055ef7 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:30 -0800 Subject: [PATCH] bitops: mips: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - unless defined(CONFIG_CPU_MIPS32) or defined(CONFIG_CPU_MIPS64) - remove __ffs() - remove ffs() - remove ffz() - remove fls() - remove fls64() - remove find_{next,first}{,_zero}_bit() - remove sched_find_first_bit() - remove generic_hweight64() - remove generic_hweight{32,16,8}() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove ext2_{set,clear}_bit_atomic() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/mips/Kconfig | 8 + include/asm-mips/bitops.h | 465 ++-------------------------------------------- 2 files changed, 24 insertions(+), 449 deletions(-) (limited to 'include') diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index ac2012f033d6..5080ea1799a4 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -801,6 +801,14 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-mips/bitops.h b/include/asm-mips/bitops.h index 0e83abc829d4..a1728f8c0705 100644 --- a/include/asm-mips/bitops.h +++ b/include/asm-mips/bitops.h @@ -104,22 +104,6 @@ static inline void set_bit(unsigned long nr, volatile unsigned long *addr) } } -/* - * __set_bit - Set a bit in memory - * @nr: the bit to set - * @addr: the address to start counting from - * - * Unlike set_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static inline void __set_bit(unsigned long nr, volatile unsigned long * addr) -{ - unsigned long * m = ((unsigned long *) addr) + (nr >> SZLONG_LOG); - - *m |= 1UL << (nr & SZLONG_MASK); -} - /* * clear_bit - Clears a bit in memory * @nr: Bit to clear @@ -168,22 +152,6 @@ static inline void clear_bit(unsigned long nr, volatile unsigned long *addr) } } -/* - * __clear_bit - Clears a bit in memory - * @nr: Bit to clear - * @addr: Address to start counting from - * - * Unlike clear_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static inline void __clear_bit(unsigned long nr, volatile unsigned long * addr) -{ - unsigned long * m = ((unsigned long *) addr) + (nr >> SZLONG_LOG); - - *m &= ~(1UL << (nr & SZLONG_MASK)); -} - /* * change_bit - Toggle a bit in memory * @nr: Bit to change @@ -234,22 +202,6 @@ static inline void change_bit(unsigned long nr, volatile unsigned long *addr) } } -/* - * __change_bit - Toggle a bit in memory - * @nr: the bit to change - * @addr: the address to start counting from - * - * Unlike change_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static inline void __change_bit(unsigned long nr, volatile unsigned long * addr) -{ - unsigned long * m = ((unsigned long *) addr) + (nr >> SZLONG_LOG); - - *m ^= 1UL << (nr & SZLONG_MASK); -} - /* * test_and_set_bit - Set a bit and return its old value * @nr: Bit to set @@ -320,30 +272,6 @@ static inline int test_and_set_bit(unsigned long nr, } } -/* - * __test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ -static inline int __test_and_set_bit(unsigned long nr, - volatile unsigned long *addr) -{ - volatile unsigned long *a = addr; - unsigned long mask; - int retval; - - a += nr >> SZLONG_LOG; - mask = 1UL << (nr & SZLONG_MASK); - retval = (mask & *a) != 0; - *a |= mask; - - return retval; -} - /* * test_and_clear_bit - Clear a bit and return its old value * @nr: Bit to clear @@ -416,30 +344,6 @@ static inline int test_and_clear_bit(unsigned long nr, } } -/* - * __test_and_clear_bit - Clear a bit and return its old value - * @nr: Bit to clear - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ -static inline int __test_and_clear_bit(unsigned long nr, - volatile unsigned long * addr) -{ - volatile unsigned long *a = addr; - unsigned long mask; - int retval; - - a += (nr >> SZLONG_LOG); - mask = 1UL << (nr & SZLONG_MASK); - retval = ((mask & *a) != 0); - *a &= ~mask; - - return retval; -} - /* * test_and_change_bit - Change a bit and return its old value * @nr: Bit to change @@ -509,43 +413,11 @@ static inline int test_and_change_bit(unsigned long nr, } } -/* - * __test_and_change_bit - Change a bit and return its old value - * @nr: Bit to change - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ -static inline int __test_and_change_bit(unsigned long nr, - volatile unsigned long *addr) -{ - volatile unsigned long *a = addr; - unsigned long mask; - int retval; - - a += (nr >> SZLONG_LOG); - mask = 1UL << (nr & SZLONG_MASK); - retval = ((mask & *a) != 0); - *a ^= mask; - - return retval; -} - #undef __bi_flags #undef __bi_local_irq_save #undef __bi_local_irq_restore -/* - * test_bit - Determine whether a bit is set - * @nr: bit number to test - * @addr: Address to start counting from - */ -static inline int test_bit(unsigned long nr, const volatile unsigned long *addr) -{ - return 1UL & (addr[nr >> SZLONG_LOG] >> (nr & SZLONG_MASK)); -} +#include /* * Return the bit position (0..63) of the most significant 1 bit in a word @@ -580,6 +452,8 @@ static inline int __ilog2(unsigned long x) return 63 - lz; } +#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) + /* * __ffs - find first bit in word. * @word: The word to search @@ -589,31 +463,7 @@ static inline int __ilog2(unsigned long x) */ static inline unsigned long __ffs(unsigned long word) { -#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) return __ilog2(word & -word); -#else - int b = 0, s; - -#ifdef CONFIG_32BIT - s = 16; if (word << 16 != 0) s = 0; b += s; word >>= s; - s = 8; if (word << 24 != 0) s = 0; b += s; word >>= s; - s = 4; if (word << 28 != 0) s = 0; b += s; word >>= s; - s = 2; if (word << 30 != 0) s = 0; b += s; word >>= s; - s = 1; if (word << 31 != 0) s = 0; b += s; - - return b; -#endif -#ifdef CONFIG_64BIT - s = 32; if (word << 32 != 0) s = 0; b += s; word >>= s; - s = 16; if (word << 48 != 0) s = 0; b += s; word >>= s; - s = 8; if (word << 56 != 0) s = 0; b += s; word >>= s; - s = 4; if (word << 60 != 0) s = 0; b += s; word >>= s; - s = 2; if (word << 62 != 0) s = 0; b += s; word >>= s; - s = 1; if (word << 63 != 0) s = 0; b += s; - - return b; -#endif -#endif } /* @@ -652,321 +502,38 @@ static inline unsigned long ffz(unsigned long word) */ static inline unsigned long fls(unsigned long word) { -#ifdef CONFIG_32BIT #ifdef CONFIG_CPU_MIPS32 __asm__ ("clz %0, %1" : "=r" (word) : "r" (word)); return 32 - word; -#else - { - int r = 32, s; - - if (word == 0) - return 0; - - s = 16; if ((word & 0xffff0000)) s = 0; r -= s; word <<= s; - s = 8; if ((word & 0xff000000)) s = 0; r -= s; word <<= s; - s = 4; if ((word & 0xf0000000)) s = 0; r -= s; word <<= s; - s = 2; if ((word & 0xc0000000)) s = 0; r -= s; word <<= s; - s = 1; if ((word & 0x80000000)) s = 0; r -= s; - - return r; - } #endif -#endif /* CONFIG_32BIT */ -#ifdef CONFIG_64BIT #ifdef CONFIG_CPU_MIPS64 - __asm__ ("dclz %0, %1" : "=r" (word) : "r" (word)); return 64 - word; -#else - { - int r = 64, s; - - if (word == 0) - return 0; - - s = 32; if ((word & 0xffffffff00000000UL)) s = 0; r -= s; word <<= s; - s = 16; if ((word & 0xffff000000000000UL)) s = 0; r -= s; word <<= s; - s = 8; if ((word & 0xff00000000000000UL)) s = 0; r -= s; word <<= s; - s = 4; if ((word & 0xf000000000000000UL)) s = 0; r -= s; word <<= s; - s = 2; if ((word & 0xc000000000000000UL)) s = 0; r -= s; word <<= s; - s = 1; if ((word & 0x8000000000000000UL)) s = 0; r -= s; - - return r; - } #endif -#endif /* CONFIG_64BIT */ } -#define fls64(x) generic_fls64(x) - -/* - * find_next_zero_bit - find the first zero bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static inline unsigned long find_next_zero_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - const unsigned long *p = addr + (offset >> SZLONG_LOG); - unsigned long result = offset & ~SZLONG_MASK; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= SZLONG_MASK; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (_MIPS_SZLONG-offset); - if (size < _MIPS_SZLONG) - goto found_first; - if (~tmp) - goto found_middle; - size -= _MIPS_SZLONG; - result += _MIPS_SZLONG; - } - while (size & ~SZLONG_MASK) { - if (~(tmp = *(p++))) - goto found_middle; - result += _MIPS_SZLONG; - size -= _MIPS_SZLONG; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. */ -found_middle: - return result + ffz(tmp); -} +#else -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) +#include +#include +#include +#include -/* - * find_next_bit - find the next set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static inline unsigned long find_next_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - const unsigned long *p = addr + (offset >> SZLONG_LOG); - unsigned long result = offset & ~SZLONG_MASK; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= SZLONG_MASK; - if (offset) { - tmp = *(p++); - tmp &= ~0UL << offset; - if (size < _MIPS_SZLONG) - goto found_first; - if (tmp) - goto found_middle; - size -= _MIPS_SZLONG; - result += _MIPS_SZLONG; - } - while (size & ~SZLONG_MASK) { - if ((tmp = *(p++))) - goto found_middle; - result += _MIPS_SZLONG; - size -= _MIPS_SZLONG; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= ~0UL >> (_MIPS_SZLONG - size); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} +#endif /*defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) */ -/* - * find_first_bit - find the first set bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first set bit, not the number of the byte - * containing a bit. - */ -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) +#include +#include #ifdef __KERNEL__ -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(const unsigned long *b) -{ -#ifdef CONFIG_32BIT - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -#endif -#ifdef CONFIG_64BIT - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 64; - return __ffs(b[2]) + 128; -#endif -} - -/* - * hweightN - returns the hamming weight of a N-bit word - * @x: the word to weigh - * - * The Hamming Weight of a number is the total number of bits set in it. - */ - -#define hweight64(x) generic_hweight64(x) -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -static inline int __test_and_set_le_bit(unsigned long nr, unsigned long *addr) -{ - unsigned char *ADDR = (unsigned char *) addr; - int mask, retval; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - retval = (mask & *ADDR) != 0; - *ADDR |= mask; - - return retval; -} - -static inline int __test_and_clear_le_bit(unsigned long nr, unsigned long *addr) -{ - unsigned char *ADDR = (unsigned char *) addr; - int mask, retval; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - retval = (mask & *ADDR) != 0; - *ADDR &= ~mask; - - return retval; -} - -static inline int test_le_bit(unsigned long nr, const unsigned long * addr) -{ - const unsigned char *ADDR = (const unsigned char *) addr; - int mask; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - - return ((mask & *ADDR) != 0); -} - -static inline unsigned long find_next_zero_le_bit(unsigned long *addr, - unsigned long size, unsigned long offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> SZLONG_LOG); - unsigned long result = offset & ~SZLONG_MASK; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= SZLONG_MASK; - if (offset) { - tmp = cpu_to_lelongp(p++); - tmp |= ~0UL >> (_MIPS_SZLONG-offset); /* bug or feature ? */ - if (size < _MIPS_SZLONG) - goto found_first; - if (~tmp) - goto found_middle; - size -= _MIPS_SZLONG; - result += _MIPS_SZLONG; - } - while (size & ~SZLONG_MASK) { - if (~(tmp = cpu_to_lelongp(p++))) - goto found_middle; - result += _MIPS_SZLONG; - size -= _MIPS_SZLONG; - } - if (!size) - return result; - tmp = cpu_to_lelongp(p); - -found_first: - tmp |= ~0UL << size; - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. */ - -found_middle: - return result + ffz(tmp); -} - -#define find_first_zero_le_bit(addr, size) \ - find_next_zero_le_bit((addr), (size), 0) - -#define ext2_set_bit(nr,addr) \ - __test_and_set_le_bit((nr),(unsigned long*)addr) -#define ext2_clear_bit(nr, addr) \ - __test_and_clear_le_bit((nr),(unsigned long*)addr) - #define ext2_set_bit_atomic(lock, nr, addr) \ -({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_set_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ -}) - -#define ext2_clear_bit_atomic(lock, nr, addr) \ -({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_clear_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ -}) -#define ext2_test_bit(nr, addr) test_le_bit((nr),(unsigned long*)addr) -#define ext2_find_first_zero_bit(addr, size) \ - find_first_zero_le_bit((unsigned long*)addr, size) -#define ext2_find_next_zero_bit(addr, size, off) \ - find_next_zero_le_bit((unsigned long*)addr, size, off) - -/* - * Bitmap functions for the minix filesystem. - * - * FIXME: These assume that Minix uses the native byte/bitorder. - * This limits the Minix filesystem's value for data exchange very much. - */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) __set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) +#include +#include +#include +#include +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From 59e18a2e1c8f6642c307032939daaf474c16344e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:31 -0800 Subject: [PATCH] bitops: parisc: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove ffz() - remove generic_fls64() - remove generic_hweight{32,16,8}() - remove generic_hweight64() - remove sched_find_first_bit() - remove find_{next,first}{,_zero}_bit() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() Signed-off-by: Akinobu Mita Cc: Kyle McMartin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/parisc/Kconfig | 8 ++ include/asm-parisc/bitops.h | 286 ++------------------------------------------ 2 files changed, 17 insertions(+), 277 deletions(-) (limited to 'include') diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index eca33cfa8a4c..6b3c50964ca9 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -25,6 +25,14 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-parisc/bitops.h b/include/asm-parisc/bitops.h index ca6119af20af..900561922c4c 100644 --- a/include/asm-parisc/bitops.h +++ b/include/asm-parisc/bitops.h @@ -35,13 +35,6 @@ static __inline__ void set_bit(int nr, volatile unsigned long * addr) _atomic_spin_unlock_irqrestore(addr, flags); } -static __inline__ void __set_bit(unsigned long nr, volatile unsigned long * addr) -{ - unsigned long *m = (unsigned long *) addr + (nr >> SHIFT_PER_LONG); - - *m |= 1UL << CHOP_SHIFTCOUNT(nr); -} - static __inline__ void clear_bit(int nr, volatile unsigned long * addr) { unsigned long mask = ~(1UL << CHOP_SHIFTCOUNT(nr)); @@ -53,13 +46,6 @@ static __inline__ void clear_bit(int nr, volatile unsigned long * addr) _atomic_spin_unlock_irqrestore(addr, flags); } -static __inline__ void __clear_bit(unsigned long nr, volatile unsigned long * addr) -{ - unsigned long *m = (unsigned long *) addr + (nr >> SHIFT_PER_LONG); - - *m &= ~(1UL << CHOP_SHIFTCOUNT(nr)); -} - static __inline__ void change_bit(int nr, volatile unsigned long * addr) { unsigned long mask = 1UL << CHOP_SHIFTCOUNT(nr); @@ -71,13 +57,6 @@ static __inline__ void change_bit(int nr, volatile unsigned long * addr) _atomic_spin_unlock_irqrestore(addr, flags); } -static __inline__ void __change_bit(unsigned long nr, volatile unsigned long * addr) -{ - unsigned long *m = (unsigned long *) addr + (nr >> SHIFT_PER_LONG); - - *m ^= 1UL << CHOP_SHIFTCOUNT(nr); -} - static __inline__ int test_and_set_bit(int nr, volatile unsigned long * addr) { unsigned long mask = 1UL << CHOP_SHIFTCOUNT(nr); @@ -93,18 +72,6 @@ static __inline__ int test_and_set_bit(int nr, volatile unsigned long * addr) return (oldbit & mask) ? 1 : 0; } -static __inline__ int __test_and_set_bit(int nr, volatile unsigned long * address) -{ - unsigned long mask = 1UL << CHOP_SHIFTCOUNT(nr); - unsigned long oldbit; - unsigned long *addr = (unsigned long *)address + (nr >> SHIFT_PER_LONG); - - oldbit = *addr; - *addr = oldbit | mask; - - return (oldbit & mask) ? 1 : 0; -} - static __inline__ int test_and_clear_bit(int nr, volatile unsigned long * addr) { unsigned long mask = 1UL << CHOP_SHIFTCOUNT(nr); @@ -120,18 +87,6 @@ static __inline__ int test_and_clear_bit(int nr, volatile unsigned long * addr) return (oldbit & mask) ? 1 : 0; } -static __inline__ int __test_and_clear_bit(int nr, volatile unsigned long * address) -{ - unsigned long mask = 1UL << CHOP_SHIFTCOUNT(nr); - unsigned long *addr = (unsigned long *)address + (nr >> SHIFT_PER_LONG); - unsigned long oldbit; - - oldbit = *addr; - *addr = oldbit & ~mask; - - return (oldbit & mask) ? 1 : 0; -} - static __inline__ int test_and_change_bit(int nr, volatile unsigned long * addr) { unsigned long mask = 1UL << CHOP_SHIFTCOUNT(nr); @@ -147,25 +102,7 @@ static __inline__ int test_and_change_bit(int nr, volatile unsigned long * addr) return (oldbit & mask) ? 1 : 0; } -static __inline__ int __test_and_change_bit(int nr, volatile unsigned long * address) -{ - unsigned long mask = 1UL << CHOP_SHIFTCOUNT(nr); - unsigned long *addr = (unsigned long *)address + (nr >> SHIFT_PER_LONG); - unsigned long oldbit; - - oldbit = *addr; - *addr = oldbit ^ mask; - - return (oldbit & mask) ? 1 : 0; -} - -static __inline__ int test_bit(int nr, const volatile unsigned long *address) -{ - unsigned long mask = 1UL << CHOP_SHIFTCOUNT(nr); - const unsigned long *addr = (const unsigned long *)address + (nr >> SHIFT_PER_LONG); - - return !!(*addr & mask); -} +#include #ifdef __KERNEL__ @@ -219,8 +156,7 @@ static __inline__ unsigned long __ffs(unsigned long x) return ret; } -/* Undefined if no bit is zero. */ -#define ffz(x) __ffs(~(x)) +#include /* * ffs: find first bit set. returns 1 to BITS_PER_LONG or 0 (if none set) @@ -263,155 +199,22 @@ static __inline__ int fls(int x) return ret; } -#define fls64(x) generic_fls64(x) -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ -#define hweight64(x) generic_hweight64(x) -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(const unsigned long *b) -{ -#ifdef __LP64__ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 64; - return __ffs(b[2]) + 128; -#else - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -#endif -} +#include +#include +#include #endif /* __KERNEL__ */ -/* - * This implementation of find_{first,next}_zero_bit was stolen from - * Linus' asm-alpha/bitops.h. - */ -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) - -static __inline__ unsigned long find_next_zero_bit(const void * addr, unsigned long size, unsigned long offset) -{ - const unsigned long * p = ((unsigned long *) addr) + (offset >> SHIFT_PER_LONG); - unsigned long result = offset & ~(BITS_PER_LONG-1); - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= (BITS_PER_LONG-1); - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (BITS_PER_LONG-offset); - if (size < BITS_PER_LONG) - goto found_first; - if (~tmp) - goto found_middle; - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - } - while (size & ~(BITS_PER_LONG -1)) { - if (~(tmp = *(p++))) - goto found_middle; - result += BITS_PER_LONG; - size -= BITS_PER_LONG; - } - if (!size) - return result; - tmp = *p; -found_first: - tmp |= ~0UL << size; -found_middle: - return result + ffz(tmp); -} - -static __inline__ unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset) -{ - const unsigned long *p = addr + (offset >> SHIFT_PER_LONG); - unsigned long result = offset & ~(BITS_PER_LONG-1); - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= (BITS_PER_LONG-1); - if (offset) { - tmp = *(p++); - tmp &= (~0UL << offset); - if (size < BITS_PER_LONG) - goto found_first; - if (tmp) - goto found_middle; - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - } - while (size & ~(BITS_PER_LONG-1)) { - if ((tmp = *(p++))) - goto found_middle; - result += BITS_PER_LONG; - size -= BITS_PER_LONG; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= (~0UL >> (BITS_PER_LONG - size)); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} - -/** - * find_first_bit - find the first set bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first set bit, not the number of the byte - * containing a bit. - */ -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - -#define _EXT2_HAVE_ASM_BITOPS_ +#include #ifdef __KERNEL__ -/* - * test_and_{set,clear}_bit guarantee atomicity without - * disabling interrupts. - */ + +#include /* '3' is bits per byte */ #define LE_BYTE_ADDR ((sizeof(unsigned long) - 1) << 3) -#define ext2_test_bit(nr, addr) \ - test_bit((nr) ^ LE_BYTE_ADDR, (unsigned long *)addr) -#define ext2_set_bit(nr, addr) \ - __test_and_set_bit((nr) ^ LE_BYTE_ADDR, (unsigned long *)addr) -#define ext2_clear_bit(nr, addr) \ - __test_and_clear_bit((nr) ^ LE_BYTE_ADDR, (unsigned long *)addr) - #define ext2_set_bit_atomic(l,nr,addr) \ test_and_set_bit((nr) ^ LE_BYTE_ADDR, (unsigned long *)addr) #define ext2_clear_bit_atomic(l,nr,addr) \ @@ -419,77 +222,6 @@ found_middle: #endif /* __KERNEL__ */ - -#define ext2_find_first_zero_bit(addr, size) \ - ext2_find_next_zero_bit((addr), (size), 0) - -/* include/linux/byteorder does not support "unsigned long" type */ -static inline unsigned long ext2_swabp(unsigned long * x) -{ -#ifdef __LP64__ - return (unsigned long) __swab64p((u64 *) x); -#else - return (unsigned long) __swab32p((u32 *) x); -#endif -} - -/* include/linux/byteorder doesn't support "unsigned long" type */ -static inline unsigned long ext2_swab(unsigned long y) -{ -#ifdef __LP64__ - return (unsigned long) __swab64((u64) y); -#else - return (unsigned long) __swab32((u32) y); -#endif -} - -static __inline__ unsigned long ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) -{ - unsigned long *p = (unsigned long *) addr + (offset >> SHIFT_PER_LONG); - unsigned long result = offset & ~(BITS_PER_LONG - 1); - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= (BITS_PER_LONG - 1UL); - if (offset) { - tmp = ext2_swabp(p++); - tmp |= (~0UL >> (BITS_PER_LONG - offset)); - if (size < BITS_PER_LONG) - goto found_first; - if (~tmp) - goto found_middle; - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - } - - while (size & ~(BITS_PER_LONG - 1)) { - if (~(tmp = *(p++))) - goto found_middle_swap; - result += BITS_PER_LONG; - size -= BITS_PER_LONG; - } - if (!size) - return result; - tmp = ext2_swabp(p); -found_first: - tmp |= ~0UL << size; - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. Skip ffz */ -found_middle: - return result + ffz(tmp); - -found_middle_swap: - return result + ffz(ext2_swab(tmp)); -} - - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) ext2_set_bit(nr,addr) -#define minix_set_bit(nr,addr) ((void)ext2_set_bit(nr,addr)) -#define minix_test_and_clear_bit(nr,addr) ext2_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) ext2_test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) ext2_find_first_zero_bit(addr,size) +#include #endif /* _PARISC_BITOPS_H */ -- cgit v1.2.3 From e779b2f95f3786cd9cfc804cd6f04f7267d75541 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:33 -0800 Subject: [PATCH] bitops: powerpc: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove generic_fls64() - remove generic_hweight{64,32,16,8}() - remove sched_find_first_bit() Signed-off-by: Akinobu Mita Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/Kconfig | 4 ++ include/asm-powerpc/bitops.h | 105 ++----------------------------------------- 2 files changed, 8 insertions(+), 101 deletions(-) (limited to 'include') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index fae42da7468d..a433b7126d33 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -37,6 +37,10 @@ config RWSEM_XCHGADD_ALGORITHM bool default y +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-powerpc/bitops.h b/include/asm-powerpc/bitops.h index bf6941a810b8..d1c2a4405660 100644 --- a/include/asm-powerpc/bitops.h +++ b/include/asm-powerpc/bitops.h @@ -184,72 +184,7 @@ static __inline__ void set_bits(unsigned long mask, unsigned long *addr) : "cc"); } -/* Non-atomic versions */ -static __inline__ int test_bit(unsigned long nr, - __const__ volatile unsigned long *addr) -{ - return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); -} - -static __inline__ void __set_bit(unsigned long nr, - volatile unsigned long *addr) -{ - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - - *p |= mask; -} - -static __inline__ void __clear_bit(unsigned long nr, - volatile unsigned long *addr) -{ - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - - *p &= ~mask; -} - -static __inline__ void __change_bit(unsigned long nr, - volatile unsigned long *addr) -{ - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - - *p ^= mask; -} - -static __inline__ int __test_and_set_bit(unsigned long nr, - volatile unsigned long *addr) -{ - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - unsigned long old = *p; - - *p = old | mask; - return (old & mask) != 0; -} - -static __inline__ int __test_and_clear_bit(unsigned long nr, - volatile unsigned long *addr) -{ - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - unsigned long old = *p; - - *p = old & ~mask; - return (old & mask) != 0; -} - -static __inline__ int __test_and_change_bit(unsigned long nr, - volatile unsigned long *addr) -{ - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - unsigned long old = *p; - - *p = old ^ mask; - return (old & mask) != 0; -} +#include /* * Return the zero-based bit position (LE, not IBM bit numbering) of @@ -310,16 +245,9 @@ static __inline__ int fls(unsigned int x) asm ("cntlzw %0,%1" : "=r" (lz) : "r" (x)); return 32 - lz; } -#define fls64(x) generic_fls64(x) +#include -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ -#define hweight64(x) generic_hweight64(x) -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include #define find_first_zero_bit(addr, size) find_next_zero_bit((addr), (size), 0) unsigned long find_next_zero_bit(const unsigned long *addr, @@ -397,32 +325,7 @@ unsigned long find_next_zero_le_bit(const unsigned long *addr, #define minix_find_first_zero_bit(addr,size) \ find_first_zero_le_bit((unsigned long *)addr, size) -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(const unsigned long *b) -{ -#ifdef CONFIG_PPC64 - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 64; - return __ffs(b[2]) + 128; -#else - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -#endif -} +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From 7e33db4e2e9d67ee01d105e33fb773eed8ba10f0 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:34 -0800 Subject: [PATCH] bitops: s390: use generic bitops - remove generic_ffs() - remove generic_fls() - remove generic_fls64() - remove generic_hweight{64,32,16,8}() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: Heiko Carstens Cc: Martin Schwidefsky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/s390/Kconfig | 4 ++++ include/asm-s390/bitops.h | 44 +++++--------------------------------------- 2 files changed, 9 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 2b7364ed23bc..01c5c082f970 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -14,6 +14,10 @@ config RWSEM_XCHGADD_ALGORITHM bool default y +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-s390/bitops.h b/include/asm-s390/bitops.h index 6b6a01dfc8dc..ca092ffb7a95 100644 --- a/include/asm-s390/bitops.h +++ b/include/asm-s390/bitops.h @@ -828,35 +828,12 @@ static inline int sched_find_first_bit(unsigned long *b) return find_first_bit(b, 140); } -/* - * ffs: find first bit set. This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ -#define ffs(x) generic_ffs(x) +#include -/* - * fls: find last bit set. - */ -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ -#define hweight64(x) \ -({ \ - unsigned long __x = (x); \ - unsigned int __w; \ - __w = generic_hweight32((unsigned int) __x); \ - __w += generic_hweight32((unsigned int) (__x>>32)); \ - __w; \ -}) -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include +#include +#include #ifdef __KERNEL__ @@ -1011,18 +988,7 @@ ext2_find_next_zero_bit(void *vaddr, unsigned long size, unsigned long offset) return offset + ext2_find_first_zero_bit(p, size); } -/* Bitmap functions for the minix filesystem. */ -/* FIXME !!! */ -#define minix_test_and_set_bit(nr,addr) \ - __test_and_set_bit(nr,(unsigned long *)addr) -#define minix_set_bit(nr,addr) \ - __set_bit(nr,(unsigned long *)addr) -#define minix_test_and_clear_bit(nr,addr) \ - __test_and_clear_bit(nr,(unsigned long *)addr) -#define minix_test_bit(nr,addr) \ - test_bit(nr,(unsigned long *)addr) -#define minix_find_first_zero_bit(addr,size) \ - find_first_zero_bit(addr,size) +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From e2268c7129e6cab53a5dac1ab0790a547555e21a Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:35 -0800 Subject: [PATCH] bitops: sh: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove find_{next,first}{,_zero}_bit() - remove generic_ffs() - remove generic_hweight{32,16,8}() - remove sched_find_first_bit() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove ext2_{set,clear}_bit_atomic() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() - remove generic_fls() - remove generic_fls64() Signed-off-by: Akinobu Mita Cc: Paul Mundt Cc: Kazumoto Kojima Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sh/Kconfig | 8 ++ include/asm-sh/bitops.h | 342 ++---------------------------------------------- 2 files changed, 18 insertions(+), 332 deletions(-) (limited to 'include') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index e9b275d90737..58583f459471 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -21,6 +21,14 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_HARDIRQS bool default y diff --git a/include/asm-sh/bitops.h b/include/asm-sh/bitops.h index f8d504e7d9d6..e34f82508568 100644 --- a/include/asm-sh/bitops.h +++ b/include/asm-sh/bitops.h @@ -19,16 +19,6 @@ static __inline__ void set_bit(int nr, volatile void * addr) local_irq_restore(flags); } -static __inline__ void __set_bit(int nr, volatile void * addr) -{ - int mask; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - *a |= mask; -} - /* * clear_bit() doesn't provide any barrier for the compiler. */ @@ -47,16 +37,6 @@ static __inline__ void clear_bit(int nr, volatile void * addr) local_irq_restore(flags); } -static __inline__ void __clear_bit(int nr, volatile void * addr) -{ - int mask; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - *a &= ~mask; -} - static __inline__ void change_bit(int nr, volatile void * addr) { int mask; @@ -70,16 +50,6 @@ static __inline__ void change_bit(int nr, volatile void * addr) local_irq_restore(flags); } -static __inline__ void __change_bit(int nr, volatile void * addr) -{ - int mask; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - *a ^= mask; -} - static __inline__ int test_and_set_bit(int nr, volatile void * addr) { int mask, retval; @@ -96,19 +66,6 @@ static __inline__ int test_and_set_bit(int nr, volatile void * addr) return retval; } -static __inline__ int __test_and_set_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *a) != 0; - *a |= mask; - - return retval; -} - static __inline__ int test_and_clear_bit(int nr, volatile void * addr) { int mask, retval; @@ -125,19 +82,6 @@ static __inline__ int test_and_clear_bit(int nr, volatile void * addr) return retval; } -static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *a) != 0; - *a &= ~mask; - - return retval; -} - static __inline__ int test_and_change_bit(int nr, volatile void * addr) { int mask, retval; @@ -154,23 +98,7 @@ static __inline__ int test_and_change_bit(int nr, volatile void * addr) return retval; } -static __inline__ int __test_and_change_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *a) != 0; - *a ^= mask; - - return retval; -} - -static __inline__ int test_bit(int nr, const volatile void *addr) -{ - return 1UL & (((const volatile unsigned int *) addr)[nr >> 5] >> (nr & 31)); -} +#include static __inline__ unsigned long ffz(unsigned long word) { @@ -206,265 +134,15 @@ static __inline__ unsigned long __ffs(unsigned long word) return result; } -/** - * find_next_bit - find the next set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static __inline__ unsigned long find_next_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - unsigned int *p = ((unsigned int *) addr) + (offset >> 5); - unsigned int result = offset & ~31UL; - unsigned int tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *p++; - tmp &= ~0UL << offset; - if (size < 32) - goto found_first; - if (tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size >= 32) { - if ((tmp = *p++) != 0) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= ~0UL >> (32 - size); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} - -/** - * find_first_bit - find the first set bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first set bit, not the number of the byte - * containing a bit. - */ -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - -static __inline__ int find_next_zero_bit(const unsigned long *addr, int size, int offset) -{ - const unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; -found_middle: - return result + ffz(tmp); -} - -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) - -/* - * ffs: find first bit set. This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ - -#define ffs(x) generic_ffs(x) - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ - -static inline int sched_find_first_bit(const unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} - -#ifdef __LITTLE_ENDIAN__ -#define ext2_set_bit(nr, addr) __test_and_set_bit((nr), (addr)) -#define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr), (addr)) -#define ext2_test_bit(nr, addr) test_bit((nr), (addr)) -#define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr), (size)) -#define ext2_find_next_zero_bit(addr, size, offset) \ - find_next_zero_bit((unsigned long *)(addr), (size), (offset)) -#else -static __inline__ int ext2_set_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned char *ADDR = (unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - retval = (mask & *ADDR) != 0; - *ADDR |= mask; - return retval; -} - -static __inline__ int ext2_clear_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned char *ADDR = (unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - retval = (mask & *ADDR) != 0; - *ADDR &= ~mask; - return retval; -} - -static __inline__ int ext2_test_bit(int nr, const volatile void * addr) -{ - int mask; - const volatile unsigned char *ADDR = (const unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - return ((mask & *ADDR) != 0); -} - -#define ext2_find_first_zero_bit(addr, size) \ - ext2_find_next_zero_bit((addr), (size), 0) - -static __inline__ unsigned long ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if(offset) { - /* We hold the little endian value in tmp, but then the - * shift is illegal. So we could keep a big endian value - * in tmp, like this: - * - * tmp = __swab32(*(p++)); - * tmp |= ~0UL >> (32-offset); - * - * but this would decrease preformance, so we change the - * shift: - */ - tmp = *(p++); - tmp |= __swab32(~0UL >> (32-offset)); - if(size < 32) - goto found_first; - if(~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while(size & ~31UL) { - if(~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if(!size) - return result; - tmp = *p; - -found_first: - /* tmp is little endian, so we would have to swab the shift, - * see above. But then we have to swab tmp below for ffz, so - * we might as well do this here. - */ - return result + ffz(__swab32(tmp) | (~0UL << size)); -found_middle: - return result + ffz(__swab32(tmp)); -} -#endif - -#define ext2_set_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_set_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -#define ext2_clear_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_clear_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) __set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) - -/* - * fls: find last bit set. - */ - -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From 62f1b2465bdf77bb95037a5418040edb2af142d0 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:37 -0800 Subject: [PATCH] bitops: sh64: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove __ffs() - remove find_{next,first}{,_zero}_bit() - remove generic_hweight{32,16,8}() - remove sched_find_first_bit() - remove generic_ffs() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove ext2_{set,clear}_bit_atomic() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() - remove generic_fls() - remove generic_fls64() Signed-off-by: Akinobu Mita Cc: Paul Mundt Cc: Richard Curnow Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sh64/Kconfig | 8 + include/asm-sh64/bitops.h | 384 ++-------------------------------------------- 2 files changed, 19 insertions(+), 373 deletions(-) (limited to 'include') diff --git a/arch/sh64/Kconfig b/arch/sh64/Kconfig index 07b172deb872..58c678e06667 100644 --- a/arch/sh64/Kconfig +++ b/arch/sh64/Kconfig @@ -21,6 +21,14 @@ config RWSEM_GENERIC_SPINLOCK bool default y +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-sh64/bitops.h b/include/asm-sh64/bitops.h index 5622b1a50cb1..f3bdcdb5d046 100644 --- a/include/asm-sh64/bitops.h +++ b/include/asm-sh64/bitops.h @@ -31,16 +31,6 @@ static __inline__ void set_bit(int nr, volatile void * addr) local_irq_restore(flags); } -static inline void __set_bit(int nr, void *addr) -{ - int mask; - unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - *a |= mask; -} - /* * clear_bit() doesn't provide any barrier for the compiler. */ @@ -58,15 +48,6 @@ static inline void clear_bit(int nr, volatile unsigned long *a) local_irq_restore(flags); } -static inline void __clear_bit(int nr, volatile unsigned long *a) -{ - int mask; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - *a &= ~mask; -} - static __inline__ void change_bit(int nr, volatile void * addr) { int mask; @@ -80,16 +61,6 @@ static __inline__ void change_bit(int nr, volatile void * addr) local_irq_restore(flags); } -static __inline__ void __change_bit(int nr, volatile void * addr) -{ - int mask; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - *a ^= mask; -} - static __inline__ int test_and_set_bit(int nr, volatile void * addr) { int mask, retval; @@ -106,19 +77,6 @@ static __inline__ int test_and_set_bit(int nr, volatile void * addr) return retval; } -static __inline__ int __test_and_set_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *a) != 0; - *a |= mask; - - return retval; -} - static __inline__ int test_and_clear_bit(int nr, volatile void * addr) { int mask, retval; @@ -135,19 +93,6 @@ static __inline__ int test_and_clear_bit(int nr, volatile void * addr) return retval; } -static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *a) != 0; - *a &= ~mask; - - return retval; -} - static __inline__ int test_and_change_bit(int nr, volatile void * addr) { int mask, retval; @@ -164,23 +109,7 @@ static __inline__ int test_and_change_bit(int nr, volatile void * addr) return retval; } -static __inline__ int __test_and_change_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned int *a = addr; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *a) != 0; - *a ^= mask; - - return retval; -} - -static __inline__ int test_bit(int nr, const volatile void *addr) -{ - return 1UL & (((const volatile unsigned int *) addr)[nr >> 5] >> (nr & 31)); -} +#include static __inline__ unsigned long ffz(unsigned long word) { @@ -204,307 +133,16 @@ static __inline__ unsigned long ffz(unsigned long word) return result; } -/** - * __ffs - find first bit in word - * @word: The word to search - * - * Undefined if no bit exists, so code should check against 0 first. - */ -static inline unsigned long __ffs(unsigned long word) -{ - int r = 0; - - if (!word) - return 0; - if (!(word & 0xffff)) { - word >>= 16; - r += 16; - } - if (!(word & 0xff)) { - word >>= 8; - r += 8; - } - if (!(word & 0xf)) { - word >>= 4; - r += 4; - } - if (!(word & 3)) { - word >>= 2; - r += 2; - } - if (!(word & 1)) { - word >>= 1; - r += 1; - } - return r; -} - -/** - * find_next_bit - find the next set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static inline unsigned long find_next_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - unsigned int *p = ((unsigned int *) addr) + (offset >> 5); - unsigned int result = offset & ~31UL; - unsigned int tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *p++; - tmp &= ~0UL << offset; - if (size < 32) - goto found_first; - if (tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size >= 32) { - if ((tmp = *p++) != 0) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= ~0UL >> (32 - size); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} - -/** - * find_first_bit - find the first set bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first set bit, not the number of the byte - * containing a bit. - */ -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - - -static inline int find_next_zero_bit(void *addr, int size, int offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; -found_middle: - return result + ffz(tmp); -} - -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ - -static inline int sched_find_first_bit(unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} - -/* - * ffs: find first bit set. This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ - -#define ffs(x) generic_ffs(x) - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -#ifdef __LITTLE_ENDIAN__ -#define ext2_set_bit(nr, addr) __test_and_set_bit((nr), (addr)) -#define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr), (addr)) -#define ext2_test_bit(nr, addr) test_bit((nr), (addr)) -#define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr), (size)) -#define ext2_find_next_zero_bit(addr, size, offset) \ - find_next_zero_bit((addr), (size), (offset)) -#else -static __inline__ int ext2_set_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned char *ADDR = (unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - retval = (mask & *ADDR) != 0; - *ADDR |= mask; - return retval; -} - -static __inline__ int ext2_clear_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned char *ADDR = (unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - retval = (mask & *ADDR) != 0; - *ADDR &= ~mask; - return retval; -} - -static __inline__ int ext2_test_bit(int nr, const volatile void * addr) -{ - int mask; - const volatile unsigned char *ADDR = (const unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - return ((mask & *ADDR) != 0); -} - -#define ext2_find_first_zero_bit(addr, size) \ - ext2_find_next_zero_bit((addr), (size), 0) - -static __inline__ unsigned long ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if(offset) { - /* We hold the little endian value in tmp, but then the - * shift is illegal. So we could keep a big endian value - * in tmp, like this: - * - * tmp = __swab32(*(p++)); - * tmp |= ~0UL >> (32-offset); - * - * but this would decrease preformance, so we change the - * shift: - */ - tmp = *(p++); - tmp |= __swab32(~0UL >> (32-offset)); - if(size < 32) - goto found_first; - if(~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while(size & ~31UL) { - if(~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if(!size) - return result; - tmp = *p; - -found_first: - /* tmp is little endian, so we would have to swab the shift, - * see above. But then we have to swab tmp below for ffz, so - * we might as well do this here. - */ - return result + ffz(__swab32(tmp) | (~0UL << size)); -found_middle: - return result + ffz(__swab32(tmp)); -} -#endif - -#define ext2_set_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_set_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -#define ext2_clear_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_clear_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) __set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) - -#define ffs(x) generic_ffs(x) -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From d59288b75797fd982546aee7ba24a495dee128dd Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:39 -0800 Subject: [PATCH] bitops: sparc: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove ffz() - remove __ffs() - remove sched_find_first_bit() - remove ffs() - remove generic_fls() - remove generic_fls64() - remove generic_hweight{32,16,8}() - remove find_{next,first}{,_zero}_bit() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove ext2_{set,clear}_bit_atomic() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: William Lee Irwin III Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sparc/Kconfig | 8 + include/asm-sparc/bitops.h | 388 ++------------------------------------------- 2 files changed, 20 insertions(+), 376 deletions(-) (limited to 'include') diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 7c58fc1a39c4..9431e967aa45 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -150,6 +150,14 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-sparc/bitops.h b/include/asm-sparc/bitops.h index f25109d62032..04aa3318f76a 100644 --- a/include/asm-sparc/bitops.h +++ b/include/asm-sparc/bitops.h @@ -152,386 +152,22 @@ static inline void change_bit(unsigned long nr, volatile unsigned long *addr) : "memory", "cc"); } -/* - * non-atomic versions - */ -static inline void __set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = 1UL << (nr & 0x1f); - unsigned long *p = ((unsigned long *)addr) + (nr >> 5); - - *p |= mask; -} - -static inline void __clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = 1UL << (nr & 0x1f); - unsigned long *p = ((unsigned long *)addr) + (nr >> 5); - - *p &= ~mask; -} - -static inline void __change_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = 1UL << (nr & 0x1f); - unsigned long *p = ((unsigned long *)addr) + (nr >> 5); - - *p ^= mask; -} - -static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = 1UL << (nr & 0x1f); - unsigned long *p = ((unsigned long *)addr) + (nr >> 5); - unsigned long old = *p; - - *p = old | mask; - return (old & mask) != 0; -} - -static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = 1UL << (nr & 0x1f); - unsigned long *p = ((unsigned long *)addr) + (nr >> 5); - unsigned long old = *p; - - *p = old & ~mask; - return (old & mask) != 0; -} - -static inline int __test_and_change_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = 1UL << (nr & 0x1f); - unsigned long *p = ((unsigned long *)addr) + (nr >> 5); - unsigned long old = *p; - - *p = old ^ mask; - return (old & mask) != 0; -} +#include #define smp_mb__before_clear_bit() do { } while(0) #define smp_mb__after_clear_bit() do { } while(0) -/* The following routine need not be atomic. */ -static inline int test_bit(int nr, __const__ volatile unsigned long *addr) -{ - return (1UL & (((unsigned long *)addr)[nr >> 5] >> (nr & 31))) != 0UL; -} - -/* The easy/cheese version for now. */ -static inline unsigned long ffz(unsigned long word) -{ - unsigned long result = 0; - - while(word & 1) { - result++; - word >>= 1; - } - return result; -} - -/** - * __ffs - find first bit in word. - * @word: The word to search - * - * Undefined if no bit exists, so code should check against 0 first. - */ -static inline int __ffs(unsigned long word) -{ - int num = 0; - - if ((word & 0xffff) == 0) { - num += 16; - word >>= 16; - } - if ((word & 0xff) == 0) { - num += 8; - word >>= 8; - } - if ((word & 0xf) == 0) { - num += 4; - word >>= 4; - } - if ((word & 0x3) == 0) { - num += 2; - word >>= 2; - } - if ((word & 0x1) == 0) - num += 1; - return num; -} - -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(unsigned long *b) -{ - - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} - -/* - * ffs: find first bit set. This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ -static inline int ffs(int x) -{ - if (!x) - return 0; - return __ffs((unsigned long)x) + 1; -} - -/* - * fls: find last (most-significant) bit set. - * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. - */ -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -/* - * find_next_zero_bit() finds the first zero bit in a bit string of length - * 'size' bits, starting the search at bit 'offset'. This is largely based - * on Linus's ALPHA routines, which are pretty portable BTW. - */ -static inline unsigned long find_next_zero_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - const unsigned long *p = addr + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. */ -found_middle: - return result + ffz(tmp); -} - -/* - * Linus sez that gcc can optimize the following correctly, we'll see if this - * holds on the Sparc as it does for the ALPHA. - */ -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) - -/** - * find_next_bit - find the first set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - * - * Scheduler induced bitop, do not use. - */ -static inline int find_next_bit(const unsigned long *addr, int size, int offset) -{ - const unsigned long *p = addr + (offset >> 5); - int num = offset & ~0x1f; - unsigned long word; - - word = *p++; - word &= ~((1 << (offset & 0x1f)) - 1); - while (num < size) { - if (word != 0) { - return __ffs(word) + num; - } - word = *p++; - num += 0x20; - } - return num; -} - -/** - * find_first_bit - find the first set bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first set bit, not the number of the byte - * containing a bit. - */ -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - -/* - */ -static inline int test_le_bit(int nr, __const__ unsigned long * addr) -{ - __const__ unsigned char *ADDR = (__const__ unsigned char *) addr; - return (ADDR[nr >> 3] >> (nr & 7)) & 1; -} - -/* - * non-atomic versions - */ -static inline void __set_le_bit(int nr, unsigned long *addr) -{ - unsigned char *ADDR = (unsigned char *)addr; - - ADDR += nr >> 3; - *ADDR |= 1 << (nr & 0x07); -} - -static inline void __clear_le_bit(int nr, unsigned long *addr) -{ - unsigned char *ADDR = (unsigned char *)addr; - - ADDR += nr >> 3; - *ADDR &= ~(1 << (nr & 0x07)); -} - -static inline int __test_and_set_le_bit(int nr, unsigned long *addr) -{ - int mask, retval; - unsigned char *ADDR = (unsigned char *)addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - retval = (mask & *ADDR) != 0; - *ADDR |= mask; - return retval; -} - -static inline int __test_and_clear_le_bit(int nr, unsigned long *addr) -{ - int mask, retval; - unsigned char *ADDR = (unsigned char *)addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - retval = (mask & *ADDR) != 0; - *ADDR &= ~mask; - return retval; -} - -static inline unsigned long find_next_zero_le_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - const unsigned long *p = addr + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if(offset) { - tmp = *(p++); - tmp |= __swab32(~0UL >> (32-offset)); - if(size < 32) - goto found_first; - if(~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while(size & ~31UL) { - if(~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if(!size) - return result; - tmp = *p; - -found_first: - tmp = __swab32(tmp) | (~0UL << size); - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. */ - return result + ffz(tmp); - -found_middle: - return result + ffz(__swab32(tmp)); -} - -#define find_first_zero_le_bit(addr, size) \ - find_next_zero_le_bit((addr), (size), 0) - -#define ext2_set_bit(nr,addr) \ - __test_and_set_le_bit((nr),(unsigned long *)(addr)) -#define ext2_clear_bit(nr,addr) \ - __test_and_clear_le_bit((nr),(unsigned long *)(addr)) - -#define ext2_set_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_set_bit((nr), (unsigned long *)(addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -#define ext2_clear_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_clear_bit((nr), (unsigned long *)(addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -#define ext2_test_bit(nr,addr) \ - test_le_bit((nr),(unsigned long *)(addr)) -#define ext2_find_first_zero_bit(addr, size) \ - find_first_zero_le_bit((unsigned long *)(addr), (size)) -#define ext2_find_next_zero_bit(addr, size, off) \ - find_next_zero_le_bit((unsigned long *)(addr), (size), (off)) - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) \ - __test_and_set_bit((nr),(unsigned long *)(addr)) -#define minix_set_bit(nr,addr) \ - __set_bit((nr),(unsigned long *)(addr)) -#define minix_test_and_clear_bit(nr,addr) \ - __test_and_clear_bit((nr),(unsigned long *)(addr)) -#define minix_test_bit(nr,addr) \ - test_bit((nr),(unsigned long *)(addr)) -#define minix_find_first_zero_bit(addr,size) \ - find_first_zero_bit((unsigned long *)(addr),(size)) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From 2d78d4beb64eb07d50665432867971c481192ebf Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:40 -0800 Subject: [PATCH] bitops: sparc64: use generic bitops - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove ffz() - remove __ffs() - remove generic_fls() - remove generic_fls64() - remove sched_find_first_bit() - remove ffs() - unless defined(ULTRA_HAS_POPULATION_COUNT) - remove generic_hweight{64,32,16,8}() - remove find_{next,first}{,_zero}_bit() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sparc64/Kconfig | 8 ++ arch/sparc64/kernel/sparc64_ksyms.c | 5 - arch/sparc64/lib/Makefile | 2 +- arch/sparc64/lib/find_bit.c | 127 --------------------- include/asm-sparc64/bitops.h | 219 +++--------------------------------- 5 files changed, 22 insertions(+), 339 deletions(-) delete mode 100644 arch/sparc64/lib/find_bit.c (limited to 'include') diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index 267afddf63cf..d1e2fc566486 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -162,6 +162,14 @@ config RWSEM_XCHGADD_ALGORITHM bool default y +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y if !ULTRA_HAS_POPULATION_COUNT + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 9914a17651b4..c7fbbcfce824 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -175,11 +175,6 @@ EXPORT_SYMBOL(set_bit); EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(change_bit); -/* Bit searching */ -EXPORT_SYMBOL(find_next_bit); -EXPORT_SYMBOL(find_next_zero_bit); -EXPORT_SYMBOL(find_next_zero_le_bit); - EXPORT_SYMBOL(ivector_table); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); diff --git a/arch/sparc64/lib/Makefile b/arch/sparc64/lib/Makefile index 8812ded19f01..4a725d8985f1 100644 --- a/arch/sparc64/lib/Makefile +++ b/arch/sparc64/lib/Makefile @@ -14,6 +14,6 @@ lib-y := PeeCeeI.o copy_page.o clear_page.o strlen.o strncmp.o \ NGmemcpy.o NGcopy_from_user.o NGcopy_to_user.o NGpatch.o \ NGpage.o NGbzero.o \ copy_in_user.o user_fixup.o memmove.o \ - mcount.o ipcsum.o rwsem.o xor.o find_bit.o delay.o + mcount.o ipcsum.o rwsem.o xor.o delay.o obj-y += iomap.o diff --git a/arch/sparc64/lib/find_bit.c b/arch/sparc64/lib/find_bit.c deleted file mode 100644 index 6059557067b4..000000000000 --- a/arch/sparc64/lib/find_bit.c +++ /dev/null @@ -1,127 +0,0 @@ -#include - -/** - * find_next_bit - find the next set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -unsigned long find_next_bit(const unsigned long *addr, unsigned long size, - unsigned long offset) -{ - const unsigned long *p = addr + (offset >> 6); - unsigned long result = offset & ~63UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 63UL; - if (offset) { - tmp = *(p++); - tmp &= (~0UL << offset); - if (size < 64) - goto found_first; - if (tmp) - goto found_middle; - size -= 64; - result += 64; - } - while (size & ~63UL) { - if ((tmp = *(p++))) - goto found_middle; - result += 64; - size -= 64; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= (~0UL >> (64 - size)); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} - -/* find_next_zero_bit() finds the first zero bit in a bit string of length - * 'size' bits, starting the search at bit 'offset'. This is largely based - * on Linus's ALPHA routines, which are pretty portable BTW. - */ - -unsigned long find_next_zero_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - const unsigned long *p = addr + (offset >> 6); - unsigned long result = offset & ~63UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 63UL; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (64-offset); - if (size < 64) - goto found_first; - if (~tmp) - goto found_middle; - size -= 64; - result += 64; - } - while (size & ~63UL) { - if (~(tmp = *(p++))) - goto found_middle; - result += 64; - size -= 64; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. */ -found_middle: - return result + ffz(tmp); -} - -unsigned long find_next_zero_le_bit(unsigned long *addr, unsigned long size, unsigned long offset) -{ - unsigned long *p = addr + (offset >> 6); - unsigned long result = offset & ~63UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 63UL; - if(offset) { - tmp = __swab64p(p++); - tmp |= (~0UL >> (64-offset)); - if(size < 64) - goto found_first; - if(~tmp) - goto found_middle; - size -= 64; - result += 64; - } - while(size & ~63) { - if(~(tmp = __swab64p(p++))) - goto found_middle; - result += 64; - size -= 64; - } - if(!size) - return result; - tmp = __swab64p(p); -found_first: - tmp |= (~0UL << size); - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. */ -found_middle: - return result + ffz(tmp); -} diff --git a/include/asm-sparc64/bitops.h b/include/asm-sparc64/bitops.h index 2361f8736498..71944b0f09de 100644 --- a/include/asm-sparc64/bitops.h +++ b/include/asm-sparc64/bitops.h @@ -18,58 +18,7 @@ extern void set_bit(unsigned long nr, volatile unsigned long *addr); extern void clear_bit(unsigned long nr, volatile unsigned long *addr); extern void change_bit(unsigned long nr, volatile unsigned long *addr); -/* "non-atomic" versions... */ - -static inline void __set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long *m = ((unsigned long *)addr) + (nr >> 6); - - *m |= (1UL << (nr & 63)); -} - -static inline void __clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long *m = ((unsigned long *)addr) + (nr >> 6); - - *m &= ~(1UL << (nr & 63)); -} - -static inline void __change_bit(int nr, volatile unsigned long *addr) -{ - unsigned long *m = ((unsigned long *)addr) + (nr >> 6); - - *m ^= (1UL << (nr & 63)); -} - -static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long *m = ((unsigned long *)addr) + (nr >> 6); - unsigned long old = *m; - unsigned long mask = (1UL << (nr & 63)); - - *m = (old | mask); - return ((old & mask) != 0); -} - -static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long *m = ((unsigned long *)addr) + (nr >> 6); - unsigned long old = *m; - unsigned long mask = (1UL << (nr & 63)); - - *m = (old & ~mask); - return ((old & mask) != 0); -} - -static inline int __test_and_change_bit(int nr, volatile unsigned long *addr) -{ - unsigned long *m = ((unsigned long *)addr) + (nr >> 6); - unsigned long old = *m; - unsigned long mask = (1UL << (nr & 63)); - - *m = (old ^ mask); - return ((old & mask) != 0); -} +#include #ifdef CONFIG_SMP #define smp_mb__before_clear_bit() membar_storeload_loadload() @@ -79,78 +28,15 @@ static inline int __test_and_change_bit(int nr, volatile unsigned long *addr) #define smp_mb__after_clear_bit() barrier() #endif -static inline int test_bit(int nr, __const__ volatile unsigned long *addr) -{ - return (1UL & (addr[nr >> 6] >> (nr & 63))) != 0UL; -} - -/* The easy/cheese version for now. */ -static inline unsigned long ffz(unsigned long word) -{ - unsigned long result; - - result = 0; - while(word & 1) { - result++; - word >>= 1; - } - return result; -} - -/** - * __ffs - find first bit in word. - * @word: The word to search - * - * Undefined if no bit exists, so code should check against 0 first. - */ -static inline unsigned long __ffs(unsigned long word) -{ - unsigned long result = 0; - - while (!(word & 1UL)) { - result++; - word >>= 1; - } - return result; -} - -/* - * fls: find last bit set. - */ - -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) +#include +#include +#include +#include #ifdef __KERNEL__ -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is cleared. - */ -static inline int sched_find_first_bit(unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(((unsigned int)b[1]))) - return __ffs(b[1]) + 64; - if (b[1] >> 32) - return __ffs(b[1] >> 32) + 96; - return __ffs(b[2]) + 128; -} - -/* - * ffs: find first bit set. This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ -static inline int ffs(int x) -{ - if (!x) - return 0; - return __ffs((unsigned long)x) + 1; -} +#include +#include /* * hweightN: returns the hamming weight (i.e. the number @@ -193,102 +79,23 @@ static inline unsigned int hweight8(unsigned int w) #else -#define hweight64(x) generic_hweight64(x) -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include #endif #endif /* __KERNEL__ */ -/** - * find_next_bit - find the next set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -extern unsigned long find_next_bit(const unsigned long *, unsigned long, - unsigned long); - -/** - * find_first_bit - find the first set bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first set bit, not the number of the byte - * containing a bit. - */ -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - -/* find_next_zero_bit() finds the first zero bit in a bit string of length - * 'size' bits, starting the search at bit 'offset'. This is largely based - * on Linus's ALPHA routines, which are pretty portable BTW. - */ - -extern unsigned long find_next_zero_bit(const unsigned long *, - unsigned long, unsigned long); - -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) - -#define test_and_set_le_bit(nr,addr) \ - test_and_set_bit((nr) ^ 0x38, (addr)) -#define test_and_clear_le_bit(nr,addr) \ - test_and_clear_bit((nr) ^ 0x38, (addr)) - -static inline int test_le_bit(int nr, __const__ unsigned long * addr) -{ - int mask; - __const__ unsigned char *ADDR = (__const__ unsigned char *) addr; - - ADDR += nr >> 3; - mask = 1 << (nr & 0x07); - return ((mask & *ADDR) != 0); -} - -#define find_first_zero_le_bit(addr, size) \ - find_next_zero_le_bit((addr), (size), 0) - -extern unsigned long find_next_zero_le_bit(unsigned long *, unsigned long, unsigned long); +#include #ifdef __KERNEL__ -#define __set_le_bit(nr, addr) \ - __set_bit((nr) ^ 0x38, (addr)) -#define __clear_le_bit(nr, addr) \ - __clear_bit((nr) ^ 0x38, (addr)) -#define __test_and_clear_le_bit(nr, addr) \ - __test_and_clear_bit((nr) ^ 0x38, (addr)) -#define __test_and_set_le_bit(nr, addr) \ - __test_and_set_bit((nr) ^ 0x38, (addr)) +#include -#define ext2_set_bit(nr,addr) \ - __test_and_set_le_bit((nr),(unsigned long *)(addr)) #define ext2_set_bit_atomic(lock,nr,addr) \ - test_and_set_le_bit((nr),(unsigned long *)(addr)) -#define ext2_clear_bit(nr,addr) \ - __test_and_clear_le_bit((nr),(unsigned long *)(addr)) + test_and_set_bit((nr) ^ 0x38,(unsigned long *)(addr)) #define ext2_clear_bit_atomic(lock,nr,addr) \ - test_and_clear_le_bit((nr),(unsigned long *)(addr)) -#define ext2_test_bit(nr,addr) \ - test_le_bit((nr),(unsigned long *)(addr)) -#define ext2_find_first_zero_bit(addr, size) \ - find_first_zero_le_bit((unsigned long *)(addr), (size)) -#define ext2_find_next_zero_bit(addr, size, off) \ - find_next_zero_le_bit((unsigned long *)(addr), (size), (off)) + test_and_clear_bit((nr) ^ 0x38,(unsigned long *)(addr)) -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) \ - __test_and_set_bit((nr),(unsigned long *)(addr)) -#define minix_set_bit(nr,addr) \ - __set_bit((nr),(unsigned long *)(addr)) -#define minix_test_and_clear_bit(nr,addr) \ - __test_and_clear_bit((nr),(unsigned long *)(addr)) -#define minix_test_bit(nr,addr) \ - test_bit((nr),(unsigned long *)(addr)) -#define minix_find_first_zero_bit(addr,size) \ - find_first_zero_bit((unsigned long *)(addr),(size)) +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From a58259cddf9f824af27abf8960ed604bee53f7c1 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:41 -0800 Subject: [PATCH] bitops: v850: use generic bitops - remove ffz() - remove find_{next,first}{,_zero}_bit() - remove generic_ffs() - remove generic_fls() - remove generic_fls64() - remove __ffs() - remove sched_find_first_bit() - remove generic_hweight{32,16,8}() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: Miles Bader Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/v850/Kconfig | 6 ++ include/asm-v850/bitops.h | 220 +++------------------------------------------- 2 files changed, 17 insertions(+), 209 deletions(-) (limited to 'include') diff --git a/arch/v850/Kconfig b/arch/v850/Kconfig index e7fc3e500342..37ec644603ab 100644 --- a/arch/v850/Kconfig +++ b/arch/v850/Kconfig @@ -16,6 +16,12 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool default n +config GENERIC_FIND_NEXT_BIT + bool + default y +config GENERIC_HWEIGHT + bool + default y config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-v850/bitops.h b/include/asm-v850/bitops.h index 44d596e792e7..1f6fd5ab4177 100644 --- a/include/asm-v850/bitops.h +++ b/include/asm-v850/bitops.h @@ -22,25 +22,11 @@ #ifdef __KERNEL__ -/* - * The __ functions are not atomic - */ +#include /* - * ffz = Find First Zero in word. Undefined if no zero exists, - * so code should check against ~0UL first.. + * The __ functions are not atomic */ -static inline unsigned long ffz (unsigned long word) -{ - unsigned long result = 0; - - while (word & 1) { - result++; - word >>= 1; - } - return result; -} - /* In the following constant-bit-op macros, a "g" constraint is used when we really need an integer ("i" constraint). This is to avoid @@ -153,203 +139,19 @@ static inline int __test_bit (int nr, const void *addr) #define smp_mb__before_clear_bit() barrier () #define smp_mb__after_clear_bit() barrier () +#include +#include +#include +#include +#include +#include +#include -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit ((addr), (size), 0) - -static inline int find_next_zero_bit(const void *addr, int size, int offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = * (p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~ (tmp = * (p++))) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - - found_first: - tmp |= ~0UL << size; - found_middle: - return result + ffz (tmp); -} - - -/* This is the same as generic_ffs, but we can't use that because it's - inline and the #include order mucks things up. */ -static inline int generic_ffs_for_find_next_bit(int x) -{ - int r = 1; - - if (!x) - return 0; - if (!(x & 0xffff)) { - x >>= 16; - r += 16; - } - if (!(x & 0xff)) { - x >>= 8; - r += 8; - } - if (!(x & 0xf)) { - x >>= 4; - r += 4; - } - if (!(x & 3)) { - x >>= 2; - r += 2; - } - if (!(x & 1)) { - x >>= 1; - r += 1; - } - return r; -} - -/* - * Find next one bit in a bitmap reasonably efficiently. - */ -static __inline__ unsigned long find_next_bit(const unsigned long *addr, - unsigned long size, unsigned long offset) -{ - unsigned int *p = ((unsigned int *) addr) + (offset >> 5); - unsigned int result = offset & ~31UL; - unsigned int tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *p++; - tmp &= ~0UL << offset; - if (size < 32) - goto found_first; - if (tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size >= 32) { - if ((tmp = *p++) != 0) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= ~0UL >> (32 - size); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + generic_ffs_for_find_next_bit(tmp); -} - -/* - * find_first_bit - find the first set bit in a memory region - */ -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - - -#define ffs(x) generic_ffs (x) -#define fls(x) generic_fls (x) -#define fls64(x) generic_fls64(x) -#define __ffs(x) ffs(x) - - -/* - * This is just `generic_ffs' from , except that it assumes - * that at least one bit is set, and returns the real index of the bit - * (rather than the bit index + 1, like ffs does). - */ -static inline int sched_ffs(int x) -{ - int r = 0; - - if (!(x & 0xffff)) { - x >>= 16; - r += 16; - } - if (!(x & 0xff)) { - x >>= 8; - r += 8; - } - if (!(x & 0xf)) { - x >>= 4; - r += 4; - } - if (!(x & 3)) { - x >>= 2; - r += 2; - } - if (!(x & 1)) { - x >>= 1; - r += 1; - } - return r; -} - -/* - * Every architecture must define this function. It's the fastest - * way of searching a 140-bit bitmap where the first 100 bits are - * unlikely to be set. It's guaranteed that at least one of the 140 - * bits is set. - */ -static inline int sched_find_first_bit(unsigned long *b) -{ - unsigned offs = 0; - while (! *b) { - b++; - offs += 32; - } - return sched_ffs (*b) + offs; -} - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ -#define hweight32(x) generic_hweight32 (x) -#define hweight16(x) generic_hweight16 (x) -#define hweight8(x) generic_hweight8 (x) - -#define ext2_set_bit __test_and_set_bit +#include #define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) -#define ext2_clear_bit __test_and_clear_bit #define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) -#define ext2_test_bit test_bit -#define ext2_find_first_zero_bit find_first_zero_bit -#define ext2_find_next_zero_bit find_next_zero_bit -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit __test_and_set_bit -#define minix_set_bit __set_bit -#define minix_test_and_clear_bit __test_and_clear_bit -#define minix_test_bit test_bit -#define minix_find_first_zero_bit find_first_zero_bit +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From f33e2fbacce8008984db99c45120db31081577c5 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:42 -0800 Subject: [PATCH] bitops: x86_64: use generic bitops - remove sched_find_first_bit() - remove generic_hweight{64,32,16,8}() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86_64/Kconfig | 4 ++++ include/asm-x86_64/bitops.h | 42 ++++++------------------------------------ 2 files changed, 10 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 6420baeb8c1f..45efe0ca88f8 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -45,6 +45,10 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_HWEIGHT + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/include/asm-x86_64/bitops.h b/include/asm-x86_64/bitops.h index eb4df23e1e41..79212128d0f7 100644 --- a/include/asm-x86_64/bitops.h +++ b/include/asm-x86_64/bitops.h @@ -356,14 +356,7 @@ static __inline__ unsigned long __fls(unsigned long word) #ifdef __KERNEL__ -static inline int sched_find_first_bit(const unsigned long *b) -{ - if (b[0]) - return __ffs(b[0]); - if (b[1]) - return __ffs(b[1]) + 64; - return __ffs(b[2]) + 128; -} +#include /** * ffs - find first bit set @@ -412,43 +405,20 @@ static __inline__ int fls(int x) return r+1; } -/** - * hweightN - returns the hamming weight of a N-bit word - * @x: the word to weigh - * - * The Hamming Weight of a number is the total number of bits set in it. - */ - -#define hweight64(x) generic_hweight64(x) -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include #endif /* __KERNEL__ */ #ifdef __KERNEL__ -#define ext2_set_bit(nr,addr) \ - __test_and_set_bit((nr),(unsigned long*)addr) +#include + #define ext2_set_bit_atomic(lock,nr,addr) \ test_and_set_bit((nr),(unsigned long*)addr) -#define ext2_clear_bit(nr, addr) \ - __test_and_clear_bit((nr),(unsigned long*)addr) #define ext2_clear_bit_atomic(lock,nr,addr) \ test_and_clear_bit((nr),(unsigned long*)addr) -#define ext2_test_bit(nr, addr) test_bit((nr),(unsigned long*)addr) -#define ext2_find_first_zero_bit(addr, size) \ - find_first_zero_bit((unsigned long*)addr, size) -#define ext2_find_next_zero_bit(addr, size, off) \ - find_next_zero_bit((unsigned long*)addr, size, off) - -/* Bitmap functions for the minix filesystem. */ -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,(void*)addr) -#define minix_set_bit(nr,addr) __set_bit(nr,(void*)addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,(void*)addr) -#define minix_test_bit(nr,addr) test_bit(nr,(void*)addr) -#define minix_find_first_zero_bit(addr,size) \ - find_first_zero_bit((void*)addr,size) + +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From d4337aa5281a7357666f713339211fcd278f4e7a Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:43 -0800 Subject: [PATCH] bitops: xtensa: use generic bitops - remove {,test_and_}{set,clear,change}_bit() - remove __{,test_and_}{set,clear,change}_bit() and test_bit() - remove generic_fls64() - remove find_{next,first}{,_zero}_bit() - remove ext2_{set,clear,test,find_first_zero,find_next_zero}_bit() - remove generic_hweight{32,16,8}() - remove sched_find_first_bit() - remove minix_{test,set,test_and_clear,test,find_first_zero}_bit() Signed-off-by: Akinobu Mita Cc: Chris Zankel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/xtensa/Kconfig | 8 ++ include/asm-xtensa/bitops.h | 340 ++------------------------------------------ 2 files changed, 16 insertions(+), 332 deletions(-) (limited to 'include') diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index e90ef5db8913..dbeb3504c3c8 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -22,6 +22,14 @@ config RWSEM_XCHGADD_ALGORITHM bool default y +config GENERIC_FIND_NEXT_BIT + bool + default y + +config GENERIC_HWEIGHT + bool + default y + config GENERIC_HARDIRQS bool default y diff --git a/include/asm-xtensa/bitops.h b/include/asm-xtensa/bitops.h index 50b83726a497..d815649617aa 100644 --- a/include/asm-xtensa/bitops.h +++ b/include/asm-xtensa/bitops.h @@ -23,156 +23,11 @@ # error SMP not supported on this architecture #endif -static __inline__ void set_bit(int nr, volatile void * addr) -{ - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - unsigned long flags; - - local_irq_save(flags); - *a |= mask; - local_irq_restore(flags); -} - -static __inline__ void __set_bit(int nr, volatile unsigned long * addr) -{ - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - - *a |= mask; -} - -static __inline__ void clear_bit(int nr, volatile void * addr) -{ - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - unsigned long flags; - - local_irq_save(flags); - *a &= ~mask; - local_irq_restore(flags); -} - -static __inline__ void __clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - - *a &= ~mask; -} - -/* - * clear_bit() doesn't provide any barrier for the compiler. - */ - #define smp_mb__before_clear_bit() barrier() #define smp_mb__after_clear_bit() barrier() -static __inline__ void change_bit(int nr, volatile void * addr) -{ - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - unsigned long flags; - - local_irq_save(flags); - *a ^= mask; - local_irq_restore(flags); -} - -static __inline__ void __change_bit(int nr, volatile void * addr) -{ - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - - *a ^= mask; -} - -static __inline__ int test_and_set_bit(int nr, volatile void * addr) -{ - unsigned long retval; - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - unsigned long flags; - - local_irq_save(flags); - retval = (mask & *a) != 0; - *a |= mask; - local_irq_restore(flags); - - return retval; -} - -static __inline__ int __test_and_set_bit(int nr, volatile void * addr) -{ - unsigned long retval; - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - - retval = (mask & *a) != 0; - *a |= mask; - - return retval; -} - -static __inline__ int test_and_clear_bit(int nr, volatile void * addr) -{ - unsigned long retval; - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - unsigned long flags; - - local_irq_save(flags); - retval = (mask & *a) != 0; - *a &= ~mask; - local_irq_restore(flags); - - return retval; -} - -static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) -{ - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - unsigned long old = *a; - - *a = old & ~mask; - return (old & mask) != 0; -} - -static __inline__ int test_and_change_bit(int nr, volatile void * addr) -{ - unsigned long retval; - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - unsigned long flags; - - local_irq_save(flags); - - retval = (mask & *a) != 0; - *a ^= mask; - local_irq_restore(flags); - - return retval; -} - -/* - * non-atomic version; can be reordered - */ - -static __inline__ int __test_and_change_bit(int nr, volatile void *addr) -{ - unsigned long mask = 1 << (nr & 0x1f); - unsigned long *a = ((unsigned long *)addr) + (nr >> 5); - unsigned long old = *a; - - *a = old ^ mask; - return (old & mask) != 0; -} - -static __inline__ int test_bit(int nr, const volatile void *addr) -{ - return 1UL & (((const volatile unsigned int *)addr)[nr>>5] >> (nr&31)); -} +#include +#include #if XCHAL_HAVE_NSA @@ -245,202 +100,23 @@ static __inline__ int fls (unsigned int x) { return __cntlz(x); } -#define fls64(x) generic_fls64(x) - -static __inline__ int -find_next_bit(const unsigned long *addr, int size, int offset) -{ - const unsigned long *p = addr + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *p++; - tmp &= ~0UL << offset; - if (size < 32) - goto found_first; - if (tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size >= 32) { - if ((tmp = *p++) != 0) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= ~0UL >> (32 - size); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} - -/** - * find_first_bit - find the first set bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first set bit, not the number of the byte - * containing a bit. - */ - -#define find_first_bit(addr, size) \ - find_next_bit((addr), (size), 0) - -static __inline__ int -find_next_zero_bit(const unsigned long *addr, int size, int offset) -{ - const unsigned long *p = addr + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if (offset) { - tmp = *p++; - tmp |= ~0UL >> (32-offset); - if (size < 32) - goto found_first; - if (~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while (size & ~31UL) { - if (~(tmp = *p++)) - goto found_middle; - result += 32; - size -= 32; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; -found_middle: - return result + ffz(tmp); -} - -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) +#include +#include +#include #ifdef __XTENSA_EL__ -# define ext2_set_bit(nr,addr) __test_and_set_bit((nr), (addr)) # define ext2_set_bit_atomic(lock,nr,addr) test_and_set_bit((nr),(addr)) -# define ext2_clear_bit(nr,addr) __test_and_clear_bit((nr), (addr)) # define ext2_clear_bit_atomic(lock,nr,addr) test_and_clear_bit((nr),(addr)) -# define ext2_test_bit(nr,addr) test_bit((nr), (addr)) -# define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr),(size)) -# define ext2_find_next_zero_bit(addr, size, offset) \ - find_next_zero_bit((addr), (size), (offset)) #elif defined(__XTENSA_EB__) -# define ext2_set_bit(nr,addr) __test_and_set_bit((nr) ^ 0x18, (addr)) # define ext2_set_bit_atomic(lock,nr,addr) test_and_set_bit((nr) ^ 0x18, (addr)) -# define ext2_clear_bit(nr,addr) __test_and_clear_bit((nr) ^ 18, (addr)) # define ext2_clear_bit_atomic(lock,nr,addr) test_and_clear_bit((nr)^0x18,(addr)) -# define ext2_test_bit(nr,addr) test_bit((nr) ^ 0x18, (addr)) -# define ext2_find_first_zero_bit(addr, size) \ - ext2_find_next_zero_bit((addr), (size), 0) - -static __inline__ unsigned long ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) -{ - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= 31UL; - if(offset) { - /* We hold the little endian value in tmp, but then the - * shift is illegal. So we could keep a big endian value - * in tmp, like this: - * - * tmp = __swab32(*(p++)); - * tmp |= ~0UL >> (32-offset); - * - * but this would decrease preformance, so we change the - * shift: - */ - tmp = *(p++); - tmp |= __swab32(~0UL >> (32-offset)); - if(size < 32) - goto found_first; - if(~tmp) - goto found_middle; - size -= 32; - result += 32; - } - while(size & ~31UL) { - if(~(tmp = *(p++))) - goto found_middle; - result += 32; - size -= 32; - } - if(!size) - return result; - tmp = *p; - -found_first: - /* tmp is little endian, so we would have to swab the shift, - * see above. But then we have to swab tmp below for ffz, so - * we might as well do this here. - */ - return result + ffz(__swab32(tmp) | (~0UL << size)); -found_middle: - return result + ffz(__swab32(tmp)); -} - #else # error processor byte order undefined! #endif - -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -/* - * Find the first bit set in a 140-bit bitmap. - * The first 100 bits are unlikely to be set. - */ - -static inline int sched_find_first_bit(const unsigned long *b) -{ - if (unlikely(b[0])) - return __ffs(b[0]); - if (unlikely(b[1])) - return __ffs(b[1]) + 32; - if (unlikely(b[2])) - return __ffs(b[2]) + 64; - if (b[3]) - return __ffs(b[3]) + 96; - return __ffs(b[4]) + 128; -} - - -/* Bitmap functions for the minix filesystem. */ - -#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) -#define minix_set_bit(nr,addr) __set_bit(nr,addr) -#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) -#define minix_test_bit(nr,addr) test_bit(nr,addr) -#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) +#include +#include +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From f51a05c16d3d051f5e000c50bccc4be91fe5f9f3 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:50 -0800 Subject: [PATCH] bitops: update include/asm-generic/bitops.h Currently include/asm-generic/bitops.h is not referenced from anywhere. But it will be the benefit of those who are trying to port Linux to another architecture. So update it by same manner Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bitops.h | 76 ++++++++------------------------------------ 1 file changed, 13 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/include/asm-generic/bitops.h b/include/asm-generic/bitops.h index 0e6d9852008c..1f9d99193df8 100644 --- a/include/asm-generic/bitops.h +++ b/include/asm-generic/bitops.h @@ -5,77 +5,27 @@ * For the benefit of those who are trying to port Linux to another * architecture, here are some C-language equivalents. You should * recode these in the native assembly language, if at all possible. - * To guarantee atomicity, these routines call cli() and sti() to - * disable interrupts while they operate. (You have to provide inline - * routines to cli() and sti().) - * - * Also note, these routines assume that you have 32 bit longs. - * You will have to change this if you are trying to port Linux to the - * Alpha architecture or to a Cray. :-) * * C language equivalents written by Theodore Ts'o, 9/26/92 */ -extern __inline__ int set_bit(int nr,long * addr) -{ - int mask, retval; - - addr += nr >> 5; - mask = 1 << (nr & 0x1f); - cli(); - retval = (mask & *addr) != 0; - *addr |= mask; - sti(); - return retval; -} - -extern __inline__ int clear_bit(int nr, long * addr) -{ - int mask, retval; - - addr += nr >> 5; - mask = 1 << (nr & 0x1f); - cli(); - retval = (mask & *addr) != 0; - *addr &= ~mask; - sti(); - return retval; -} - -extern __inline__ int test_bit(int nr, const unsigned long * addr) -{ - int mask; - - addr += nr >> 5; - mask = 1 << (nr & 0x1f); - return ((mask & *addr) != 0); -} - -/* - * fls: find last bit set. - */ - -#define fls(x) generic_fls(x) -#define fls64(x) generic_fls64(x) +#include +#include +#include +#include +#include +#include +#include #ifdef __KERNEL__ -/* - * ffs: find first bit set. This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ - -#define ffs(x) generic_ffs(x) - -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ +#include +#include +#include -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) +#include +#include +#include #endif /* __KERNEL__ */ -- cgit v1.2.3 From 6ecf66ae49390bb790673d46e5fb723edcc3733b Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:51 -0800 Subject: [PATCH] bitops: sh: make thread_info.flags an unsigned long The test_bit() routines are defined to work on a pointer to unsigned long. But thread_info.flags is __u32 (unsigned int) on sh and it is passed to flag set/clear/test wrappers in include/linux/thread_info.h. So the compiler will print warnings. This patch changes to unsigned long instead. Signed-off-by: Akinobu Mita Cc: Paul Mundt Cc: Kazumoto Kojima Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-sh/thread_info.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-sh/thread_info.h b/include/asm-sh/thread_info.h index 85f0c11b4319..7345350d98c0 100644 --- a/include/asm-sh/thread_info.h +++ b/include/asm-sh/thread_info.h @@ -18,7 +18,7 @@ struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ - __u32 flags; /* low level flags */ + unsigned long flags; /* low level flags */ __u32 cpu; int preempt_count; /* 0 => preemptable, <0 => BUG */ struct restart_block restart_block; -- cgit v1.2.3 From e9bebd6f3acee68fa07d44726895b40733cb1dc0 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 26 Mar 2006 01:39:55 -0800 Subject: [PATCH] bitops: remove unused generic bitops in include/linux/bitops.h generic_{ffs,fls,fls64,hweight{64,32,16,8}}() were moved into include/asm-generic/bitops.h. So all architectures don't use them. Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bitops.h | 124 +------------------------------------------------ 1 file changed, 1 insertion(+), 123 deletions(-) (limited to 'include') diff --git a/include/linux/bitops.h b/include/linux/bitops.h index f17525a963d1..5d1eabcde5d5 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -2,89 +2,12 @@ #define _LINUX_BITOPS_H #include -/* - * ffs: find first bit set. This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ - -static inline int generic_ffs(int x) -{ - int r = 1; - - if (!x) - return 0; - if (!(x & 0xffff)) { - x >>= 16; - r += 16; - } - if (!(x & 0xff)) { - x >>= 8; - r += 8; - } - if (!(x & 0xf)) { - x >>= 4; - r += 4; - } - if (!(x & 3)) { - x >>= 2; - r += 2; - } - if (!(x & 1)) { - x >>= 1; - r += 1; - } - return r; -} - -/* - * fls: find last bit set. - */ - -static __inline__ int generic_fls(int x) -{ - int r = 32; - - if (!x) - return 0; - if (!(x & 0xffff0000u)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xff000000u)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xf0000000u)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xc0000000u)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x80000000u)) { - x <<= 1; - r -= 1; - } - return r; -} - /* * Include this here because some architectures need generic_ffs/fls in * scope */ #include - -static inline int generic_fls64(__u64 x) -{ - __u32 h = x >> 32; - if (h) - return fls(h) + 32; - return fls(x); -} - static __inline__ int get_bitmask_order(unsigned int count) { int order; @@ -103,54 +26,9 @@ static __inline__ int get_count_order(unsigned int count) return order; } -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -static inline unsigned int generic_hweight32(unsigned int w) -{ - unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); - res = (res & 0x33333333) + ((res >> 2) & 0x33333333); - res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); - res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); - return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); -} - -static inline unsigned int generic_hweight16(unsigned int w) -{ - unsigned int res = (w & 0x5555) + ((w >> 1) & 0x5555); - res = (res & 0x3333) + ((res >> 2) & 0x3333); - res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F); - return (res & 0x00FF) + ((res >> 8) & 0x00FF); -} - -static inline unsigned int generic_hweight8(unsigned int w) -{ - unsigned int res = (w & 0x55) + ((w >> 1) & 0x55); - res = (res & 0x33) + ((res >> 2) & 0x33); - return (res & 0x0F) + ((res >> 4) & 0x0F); -} - -static inline unsigned long generic_hweight64(__u64 w) -{ -#if BITS_PER_LONG < 64 - return generic_hweight32((unsigned int)(w >> 32)) + - generic_hweight32((unsigned int)w); -#else - u64 res; - res = (w & 0x5555555555555555ul) + ((w >> 1) & 0x5555555555555555ul); - res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul); - res = (res & 0x0F0F0F0F0F0F0F0Ful) + ((res >> 4) & 0x0F0F0F0F0F0F0F0Ful); - res = (res & 0x00FF00FF00FF00FFul) + ((res >> 8) & 0x00FF00FF00FF00FFul); - res = (res & 0x0000FFFF0000FFFFul) + ((res >> 16) & 0x0000FFFF0000FFFFul); - return (res & 0x00000000FFFFFFFFul) + ((res >> 32) & 0x00000000FFFFFFFFul); -#endif -} - static inline unsigned long hweight_long(unsigned long w) { - return sizeof(w) == 4 ? generic_hweight32(w) : generic_hweight64(w); + return sizeof(w) == 4 ? hweight32(w) : hweight64(w); } /* -- cgit v1.2.3 From 335bd9dff31d042b773591933d3ee5bd62d5ea27 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sun, 26 Mar 2006 21:25:57 +0100 Subject: [SERIAL] Remove obsoleted au1x00_uart driver As announced in feature-removal-schedule.txt. Signed-off-by: Ralf Baechle Signed-off-by: Russell King --- arch/mips/au1000/common/setup.c | 2 +- drivers/serial/Kconfig | 16 - drivers/serial/Makefile | 1 - drivers/serial/au1x00_uart.c | 1287 --------------------------------------- include/asm-mips/serial.h | 85 +-- 5 files changed, 2 insertions(+), 1389 deletions(-) delete mode 100644 drivers/serial/au1x00_uart.c (limited to 'include') diff --git a/arch/mips/au1000/common/setup.c b/arch/mips/au1000/common/setup.c index 1080558c8100..307e98c29ddc 100644 --- a/arch/mips/au1000/common/setup.c +++ b/arch/mips/au1000/common/setup.c @@ -94,7 +94,7 @@ void __init plat_setup(void) argptr = prom_getcmdline(); -#if defined(CONFIG_SERIAL_AU1X00_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) +#ifdef CONFIG_SERIAL_8250_CONSOLE if ((argptr = strstr(argptr, "console=")) == NULL) { argptr = prom_getcmdline(); strcat(argptr, " console=ttyS0,115200"); diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index ceb3697bf84d..fe0d8b8e91c8 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -620,22 +620,6 @@ config SERIAL_SH_SCI_CONSOLE depends on SERIAL_SH_SCI=y select SERIAL_CORE_CONSOLE -config SERIAL_AU1X00 - bool "Enable Au1x00 UART Support" - depends on MIPS && SOC_AU1X00 - select SERIAL_CORE - help - If you have an Alchemy AU1X00 processor (MIPS based) and you want - to use serial ports, say Y. Otherwise, say N. - -config SERIAL_AU1X00_CONSOLE - bool "Enable Au1x00 serial console" - depends on SERIAL_AU1X00 - select SERIAL_CORE_CONSOLE - help - If you have an Alchemy AU1X00 processor (MIPS based) and you want - to use a console on a serial port, say Y. Otherwise, say N. - config SERIAL_CORE tristate diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index a3a4323d9c86..d2b4c214876b 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -41,7 +41,6 @@ obj-$(CONFIG_SERIAL_COLDFIRE) += mcfserial.o obj-$(CONFIG_V850E_UART) += v850e_uart.o obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o obj-$(CONFIG_SERIAL_LH7A40X) += serial_lh7a40x.o -obj-$(CONFIG_SERIAL_AU1X00) += au1x00_uart.o obj-$(CONFIG_SERIAL_DZ) += dz.o obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o diff --git a/drivers/serial/au1x00_uart.c b/drivers/serial/au1x00_uart.c deleted file mode 100644 index 948880ac5878..000000000000 --- a/drivers/serial/au1x00_uart.c +++ /dev/null @@ -1,1287 +0,0 @@ -/* - * Driver for 8250/16550-type serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2001 Russell King. - * - * 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. - * - * A note about mapbase / membase - * - * mapbase is the physical address of the IO port. Currently, we don't - * support this very well, and it may well be dropped from this driver - * in future. As such, mapbase should be NULL. - * - * membase is an 'ioremapped' cookie. This is compatible with the old - * serial.c driver, and is currently the preferred form. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if defined(CONFIG_SERIAL_AU1X00_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include "8250.h" - -/* - * Debugging. - */ -#if 0 -#define DEBUG_AUTOCONF(fmt...) printk(fmt) -#else -#define DEBUG_AUTOCONF(fmt...) do { } while (0) -#endif - -#if 0 -#define DEBUG_INTR(fmt...) printk(fmt) -#else -#define DEBUG_INTR(fmt...) do { } while (0) -#endif - -#define PASS_LIMIT 256 - -/* - * We default to IRQ0 for the "no irq" hack. Some - * machine types want others as well - they're free - * to redefine this in their header file. - */ -#define is_real_interrupt(irq) ((irq) != 0) - -static struct old_serial_port old_serial_port[] = { - { .baud_base = 0, - .iomem_base = (u8 *)UART0_ADDR, - .irq = AU1000_UART0_INT, - .flags = STD_COM_FLAGS, - .iomem_reg_shift = 2, - }, { - .baud_base = 0, - .iomem_base = (u8 *)UART1_ADDR, - .irq = AU1000_UART1_INT, - .flags = STD_COM_FLAGS, - .iomem_reg_shift = 2 - }, { - .baud_base = 0, - .iomem_base = (u8 *)UART2_ADDR, - .irq = AU1000_UART2_INT, - .flags = STD_COM_FLAGS, - .iomem_reg_shift = 2 - }, { - .baud_base = 0, - .iomem_base = (u8 *)UART3_ADDR, - .irq = AU1000_UART3_INT, - .flags = STD_COM_FLAGS, - .iomem_reg_shift = 2 - } -}; - -#define UART_NR ARRAY_SIZE(old_serial_port) - -struct uart_8250_port { - struct uart_port port; - struct timer_list timer; /* "no irq" timer */ - struct list_head list; /* ports on this IRQ */ - unsigned short rev; - unsigned char acr; - unsigned char ier; - unsigned char lcr; - unsigned char mcr_mask; /* mask of user bits */ - unsigned char mcr_force; /* mask of forced bits */ - unsigned char lsr_break_flag; - - /* - * We provide a per-port pm hook. - */ - void (*pm)(struct uart_port *port, - unsigned int state, unsigned int old); -}; - -struct irq_info { - spinlock_t lock; - struct list_head *head; -}; - -static struct irq_info irq_lists[NR_IRQS]; - -/* - * Here we define the default xmit fifo size used for each type of UART. - */ -static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { - { "unknown", 1, 0 }, - { "8250", 1, 0 }, - { "16450", 1, 0 }, - { "16550", 1, 0 }, - /* PORT_16550A */ - { "AU1X00_UART",16, UART_CLEAR_FIFO | UART_USE_FIFO }, -}; - -static unsigned int serial_in(struct uart_8250_port *up, int offset) -{ - return au_readl((unsigned long)up->port.membase + offset); -} - -static void serial_out(struct uart_8250_port *up, int offset, int value) -{ - au_writel(value, (unsigned long)up->port.membase + offset); -} - -#define serial_inp(up, offset) serial_in(up, offset) -#define serial_outp(up, offset, value) serial_out(up, offset, value) - -/* - * This routine is called by rs_init() to initialize a specific serial - * port. It determines what type of UART chip this serial port is - * using: 8250, 16450, 16550, 16550A. The important question is - * whether or not this UART is a 16550A or not, since this will - * determine whether or not we can use its FIFO features or not. - */ -static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) -{ - unsigned char save_lcr, save_mcr; - unsigned long flags; - - if (!up->port.iobase && !up->port.mapbase && !up->port.membase) - return; - - DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%08lx): ", - up->port.line, up->port.iobase, up->port.membase); - - /* - * We really do need global IRQs disabled here - we're going to - * be frobbing the chips IRQ enable register to see if it exists. - */ - spin_lock_irqsave(&up->port.lock, flags); -// save_flags(flags); cli(); - - save_mcr = serial_in(up, UART_MCR); - save_lcr = serial_in(up, UART_LCR); - - up->port.type = PORT_16550A; - serial_outp(up, UART_LCR, save_lcr); - - up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; - - if (up->port.type == PORT_UNKNOWN) - goto out; - - /* - * Reset the UART. - */ - serial_outp(up, UART_MCR, save_mcr); - serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT)); - serial_outp(up, UART_FCR, 0); - (void)serial_in(up, UART_RX); - serial_outp(up, UART_IER, 0); - - out: - spin_unlock_irqrestore(&up->port.lock, flags); -// restore_flags(flags); - DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name); -} - -static void serial8250_stop_tx(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - - if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void serial8250_start_tx(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void serial8250_stop_rx(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); -} - -static void serial8250_enable_ms(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -static void -receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs) -{ - struct tty_struct *tty = up->port.info->tty; - unsigned char ch, flag; - int max_count = 256; - - do { - ch = serial_inp(up, UART_RX); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) { - /* - * For statistics only - */ - if (*status & UART_LSR_BI) { - *status &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (*status & UART_LSR_PE) - up->port.icount.parity++; - else if (*status & UART_LSR_FE) - up->port.icount.frame++; - if (*status & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ingored. - */ - *status &= up->port.read_status_mask; - -#ifdef CONFIG_SERIAL_AU1X00_CONSOLE - if (up->port.line == up->port.cons->index) { - /* Recover the break flag from console xmit */ - *status |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } -#endif - if (*status & UART_LSR_BI) { - DEBUG_INTR("handling break...."); - flag = TTY_BREAK; - } else if (*status & UART_LSR_PE) - flag = TTY_PARITY; - else if (*status & UART_LSR_FE) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(&up->port, ch, regs)) - goto ignore_char; - if ((*status & up->port.ignore_status_mask) == 0) - tty_insert_flip_char(tty, ch, flag); - if (*status & UART_LSR_OE) - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - ignore_char: - *status = serial_inp(up, UART_LSR); - } while ((*status & UART_LSR_DR) && (max_count-- > 0)); - spin_unlock(&up->port.lock); - tty_flip_buffer_push(tty); - spin_lock(&up->port.lock); -} - -static void transmit_chars(struct uart_8250_port *up) -{ - struct circ_buf *xmit = &up->port.info->xmit; - int count; - - if (up->port.x_char) { - serial_outp(up, UART_TX, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - serial8250_stop_tx(&up->port); - return; - } - - count = up->port.fifosize; - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - DEBUG_INTR("THRE..."); - - if (uart_circ_empty(xmit)) - serial8250_stop_tx(&up->port); -} - -static void check_modem_status(struct uart_8250_port *up) -{ - int status; - - status = serial_in(up, UART_MSR); - - if ((status & UART_MSR_ANY_DELTA) == 0) - return; - - if (status & UART_MSR_TERI) - up->port.icount.rng++; - if (status & UART_MSR_DDSR) - up->port.icount.dsr++; - if (status & UART_MSR_DDCD) - uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); - if (status & UART_MSR_DCTS) - uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - - wake_up_interruptible(&up->port.info->delta_msr_wait); -} - -/* - * This handles the interrupt from one port. - */ -static inline void -serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs) -{ - unsigned int status = serial_inp(up, UART_LSR); - - DEBUG_INTR("status = %x...", status); - - if (status & UART_LSR_DR) - receive_chars(up, &status, regs); - check_modem_status(up); - if (status & UART_LSR_THRE) - transmit_chars(up); -} - -/* - * This is the serial driver's interrupt routine. - * - * Arjan thinks the old way was overly complex, so it got simplified. - * Alan disagrees, saying that need the complexity to handle the weird - * nature of ISA shared interrupts. (This is a special exception.) - * - * In order to handle ISA shared interrupts properly, we need to check - * that all ports have been serviced, and therefore the ISA interrupt - * line has been de-asserted. - * - * This means we need to loop through all ports. checking that they - * don't have an interrupt pending. - */ -static irqreturn_t serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct irq_info *i = dev_id; - struct list_head *l, *end = NULL; - int pass_counter = 0; - - DEBUG_INTR("serial8250_interrupt(%d)...", irq); - - spin_lock(&i->lock); - - l = i->head; - do { - struct uart_8250_port *up; - unsigned int iir; - - up = list_entry(l, struct uart_8250_port, list); - - iir = serial_in(up, UART_IIR); - if (!(iir & UART_IIR_NO_INT)) { - spin_lock(&up->port.lock); - serial8250_handle_port(up, regs); - spin_unlock(&up->port.lock); - - end = NULL; - } else if (end == NULL) - end = l; - - l = l->next; - - if (l == i->head && pass_counter++ > PASS_LIMIT) { - /* If we hit this, we're dead. */ - printk(KERN_ERR "serial8250: too much work for " - "irq%d\n", irq); - break; - } - } while (l != end); - - spin_unlock(&i->lock); - - DEBUG_INTR("end.\n"); - /* FIXME! Was it really ours? */ - return IRQ_HANDLED; -} - -/* - * To support ISA shared interrupts, we need to have one interrupt - * handler that ensures that the IRQ line has been deasserted - * before returning. Failing to do this will result in the IRQ - * line being stuck active, and, since ISA irqs are edge triggered, - * no more IRQs will be seen. - */ -static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up) -{ - spin_lock_irq(&i->lock); - - if (!list_empty(i->head)) { - if (i->head == &up->list) - i->head = i->head->next; - list_del(&up->list); - } else { - BUG_ON(i->head != &up->list); - i->head = NULL; - } - - spin_unlock_irq(&i->lock); -} - -static int serial_link_irq_chain(struct uart_8250_port *up) -{ - struct irq_info *i = irq_lists + up->port.irq; - int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0; - - spin_lock_irq(&i->lock); - - if (i->head) { - list_add(&up->list, i->head); - spin_unlock_irq(&i->lock); - - ret = 0; - } else { - INIT_LIST_HEAD(&up->list); - i->head = &up->list; - spin_unlock_irq(&i->lock); - - ret = request_irq(up->port.irq, serial8250_interrupt, - irq_flags, "serial", i); - if (ret < 0) - serial_do_unlink(i, up); - } - - return ret; -} - -static void serial_unlink_irq_chain(struct uart_8250_port *up) -{ - struct irq_info *i = irq_lists + up->port.irq; - - BUG_ON(i->head == NULL); - - if (list_empty(i->head)) - free_irq(up->port.irq, i); - - serial_do_unlink(i, up); -} - -/* - * This function is used to handle ports that do not have an - * interrupt. This doesn't work very well for 16450's, but gives - * barely passable results for a 16550A. (Although at the expense - * of much CPU overhead). - */ -static void serial8250_timeout(unsigned long data) -{ - struct uart_8250_port *up = (struct uart_8250_port *)data; - unsigned int timeout; - unsigned int iir; - - iir = serial_in(up, UART_IIR); - if (!(iir & UART_IIR_NO_INT)) { - spin_lock(&up->port.lock); - serial8250_handle_port(up, NULL); - spin_unlock(&up->port.lock); - } - - timeout = up->port.timeout; - timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - mod_timer(&up->timer, jiffies + timeout); -} - -static unsigned int serial8250_tx_empty(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int serial8250_get_mctrl(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - unsigned char status; - unsigned int ret; - - status = serial_in(up, UART_MSR); - - ret = 0; - if (status & UART_MSR_DCD) - ret |= TIOCM_CAR; - if (status & UART_MSR_RI) - ret |= TIOCM_RNG; - if (status & UART_MSR_DSR) - ret |= TIOCM_DSR; - if (status & UART_MSR_CTS) - ret |= TIOCM_CTS; - return ret; -} - -static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - unsigned char mcr = 0; - - if (mctrl & TIOCM_RTS) - mcr |= UART_MCR_RTS; - if (mctrl & TIOCM_DTR) - mcr |= UART_MCR_DTR; - if (mctrl & TIOCM_OUT1) - mcr |= UART_MCR_OUT1; - if (mctrl & TIOCM_OUT2) - mcr |= UART_MCR_OUT2; - if (mctrl & TIOCM_LOOP) - mcr |= UART_MCR_LOOP; - - mcr = (mcr & up->mcr_mask) | up->mcr_force; - - serial_out(up, UART_MCR, mcr); -} - -static void serial8250_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - if (break_state == -1) - up->lcr |= UART_LCR_SBC; - else - up->lcr &= ~UART_LCR_SBC; - serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static int serial8250_startup(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - unsigned long flags; - int retval; - - /* - * Clear the FIFO buffers and disable them. - * (they will be reeanbled in set_termios()) - */ - if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) { - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_FCR, 0); - } - - /* - * Clear the interrupt registers. - */ - (void) serial_inp(up, UART_LSR); - (void) serial_inp(up, UART_RX); - (void) serial_inp(up, UART_IIR); - (void) serial_inp(up, UART_MSR); - - /* - * At this point, there's no way the LSR could still be 0xff; - * if it is, then bail out, because there's likely no UART - * here. - */ - if (!(up->port.flags & UPF_BUGGY_UART) && - (serial_inp(up, UART_LSR) == 0xff)) { - printk("ttyS%d: LSR safety check engaged!\n", up->port.line); - return -ENODEV; - } - - retval = serial_link_irq_chain(up); - if (retval) - return retval; - - /* - * Now, initialize the UART - */ - serial_outp(up, UART_LCR, UART_LCR_WLEN8); - - spin_lock_irqsave(&up->port.lock, flags); - if (up->port.flags & UPF_FOURPORT) { - if (!is_real_interrupt(up->port.irq)) - up->port.mctrl |= TIOCM_OUT1; - } else - /* - * Most PC uarts need OUT2 raised to enable interrupts. - */ - if (is_real_interrupt(up->port.irq)) - up->port.mctrl |= TIOCM_OUT2; - - serial8250_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - */ - up->ier = UART_IER_RLSI | UART_IER_RDI; - serial_outp(up, UART_IER, up->ier); - - if (up->port.flags & UPF_FOURPORT) { - unsigned int icp; - /* - * Enable interrupts on the AST Fourport board - */ - icp = (up->port.iobase & 0xfe0) | 0x01f; - outb_p(0x80, icp); - (void) inb_p(icp); - } - - /* - * And clear the interrupt registers again for luck. - */ - (void) serial_inp(up, UART_LSR); - (void) serial_inp(up, UART_RX); - (void) serial_inp(up, UART_IIR); - (void) serial_inp(up, UART_MSR); - - return 0; -} - -static void serial8250_shutdown(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - unsigned long flags; - - /* - * Disable interrupts from this port - */ - up->ier = 0; - serial_outp(up, UART_IER, 0); - - spin_lock_irqsave(&up->port.lock, flags); - if (up->port.flags & UPF_FOURPORT) { - /* reset interrupts on the AST Fourport board */ - inb((up->port.iobase & 0xfe0) | 0x1f); - up->port.mctrl |= TIOCM_OUT1; - } else - up->port.mctrl &= ~TIOCM_OUT2; - - serial8250_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Disable break condition and FIFOs - */ - serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_FCR, 0); - - /* - * Read data port to reset things, and then unlink from - * the IRQ chain. - */ - (void) serial_in(up, UART_RX); - - if (!is_real_interrupt(up->port.irq)) - del_timer_sync(&up->timer); - else - serial_unlink_irq_chain(up); -} - -static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) -{ - unsigned int quot; - - /* - * Handle magic divisors for baud rates above baud_base on - * SMSC SuperIO chips. - */ - if ((port->flags & UPF_MAGIC_MULTIPLIER) && - baud == (port->uartclk/4)) - quot = 0x8001; - else if ((port->flags & UPF_MAGIC_MULTIPLIER) && - baud == (port->uartclk/8)) - quot = 0x8002; - else - quot = uart_get_divisor(port, baud); - - return quot; -} - -static void -serial8250_set_termios(struct uart_port *port, struct termios *termios, - struct termios *old) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - unsigned char cval, fcr = 0; - unsigned long flags; - unsigned int baud, quot; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(termios->c_cflag & PARODD)) - cval |= UART_LCR_EPAR; -#ifdef CMSPAR - if (termios->c_cflag & CMSPAR) - cval |= UART_LCR_SPAR; -#endif - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = serial8250_get_divisor(port, baud); - quot = 0x35; /* FIXME */ - - /* - * Work around a bug in the Oxford Semiconductor 952 rev B - * chip which causes it to seriously miscalculate baud rates - * when DLL is 0. - */ - if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 && - up->rev == 0x5201) - quot ++; - - if (uart_config[up->port.type].flags & UART_USE_FIFO) { - if (baud < 2400) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIGGER_1; - else - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIGGER_8; - } - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* - * Characteres to ignore - */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts - */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - - serial_out(up, UART_IER, up->ier); - serial_outp(up, 0x28, quot & 0xffff); - up->lcr = cval; /* Save LCR */ - if (up->port.type != PORT_16750) { - if (fcr & UART_FCR_ENABLE_FIFO) { - /* emulated UARTs (Lucent Venus 167x) need two steps */ - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - } - serial_outp(up, UART_FCR, fcr); /* set fcr */ - } - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void -serial8250_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - if (state) { - /* sleep */ - if (up->pm) - up->pm(port, state, oldstate); - } else { - /* wake */ - if (up->pm) - up->pm(port, state, oldstate); - } -} - -/* - * Resource handling. This is complicated by the fact that resources - * depend on the port type. Maybe we should be claiming the standard - * 8250 ports, and then trying to get other resources as necessary? - */ -static int -serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res) -{ - unsigned int size = 8 << up->port.regshift; - int ret = 0; - - switch (up->port.iotype) { - case UPIO_MEM: - if (up->port.mapbase) { - *res = request_mem_region(up->port.mapbase, size, "serial"); - if (!*res) - ret = -EBUSY; - } - break; - - case UPIO_HUB6: - case UPIO_PORT: - *res = request_region(up->port.iobase, size, "serial"); - if (!*res) - ret = -EBUSY; - break; - } - return ret; -} - - -static void serial8250_release_port(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - unsigned long start, offset = 0, size = 0; - - size <<= up->port.regshift; - - switch (up->port.iotype) { - case UPIO_MEM: - if (up->port.mapbase) { - /* - * Unmap the area. - */ - iounmap(up->port.membase); - up->port.membase = NULL; - - start = up->port.mapbase; - - if (size) - release_mem_region(start + offset, size); - release_mem_region(start, 8 << up->port.regshift); - } - break; - - case UPIO_HUB6: - case UPIO_PORT: - start = up->port.iobase; - - if (size) - release_region(start + offset, size); - release_region(start + offset, 8 << up->port.regshift); - break; - - default: - break; - } -} - -static int serial8250_request_port(struct uart_port *port) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - struct resource *res = NULL, *res_rsa = NULL; - int ret = 0; - - ret = serial8250_request_std_resource(up, &res); - - /* - * If we have a mapbase, then request that as well. - */ - if (ret == 0 && up->port.flags & UPF_IOREMAP) { - int size = res->end - res->start + 1; - - up->port.membase = ioremap(up->port.mapbase, size); - if (!up->port.membase) - ret = -ENOMEM; - } - - if (ret < 0) { - if (res_rsa) - release_resource(res_rsa); - if (res) - release_resource(res); - } - return ret; -} - -static void serial8250_config_port(struct uart_port *port, int flags) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - struct resource *res_std = NULL, *res_rsa = NULL; - int probeflags = PROBE_ANY; - - probeflags &= ~PROBE_RSA; - - if (flags & UART_CONFIG_TYPE) - autoconfig(up, probeflags); - - /* - * If the port wasn't an RSA port, release the resource. - */ - if (up->port.type != PORT_RSA && res_rsa) - release_resource(res_rsa); - - if (up->port.type == PORT_UNKNOWN && res_std) - release_resource(res_std); -} - -static int -serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - if (ser->irq >= NR_IRQS || ser->irq < 0 || - ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || - ser->type > PORT_MAX_8250 || ser->type == PORT_CIRRUS || - ser->type == PORT_STARTECH) - return -EINVAL; - return 0; -} - -static const char * -serial8250_type(struct uart_port *port) -{ - int type = port->type; - - if (type >= ARRAY_SIZE(uart_config)) - type = 0; - return uart_config[type].name; -} - -static struct uart_ops serial8250_pops = { - .tx_empty = serial8250_tx_empty, - .set_mctrl = serial8250_set_mctrl, - .get_mctrl = serial8250_get_mctrl, - .stop_tx = serial8250_stop_tx, - .start_tx = serial8250_start_tx, - .stop_rx = serial8250_stop_rx, - .enable_ms = serial8250_enable_ms, - .break_ctl = serial8250_break_ctl, - .startup = serial8250_startup, - .shutdown = serial8250_shutdown, - .set_termios = serial8250_set_termios, - .pm = serial8250_pm, - .type = serial8250_type, - .release_port = serial8250_release_port, - .request_port = serial8250_request_port, - .config_port = serial8250_config_port, - .verify_port = serial8250_verify_port, -}; - -static struct uart_8250_port serial8250_ports[UART_NR]; - -static void __init serial8250_isa_init_ports(void) -{ - struct uart_8250_port *up; - static int first = 1; - int i; - - if (!first) - return; - first = 0; - - for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port); - i++, up++) { - up->port.iobase = old_serial_port[i].port; - up->port.irq = old_serial_port[i].irq; - up->port.uartclk = get_au1x00_uart_baud_base(); - up->port.flags = old_serial_port[i].flags; - up->port.hub6 = old_serial_port[i].hub6; - up->port.membase = old_serial_port[i].iomem_base; - up->port.iotype = old_serial_port[i].io_type; - up->port.regshift = old_serial_port[i].iomem_reg_shift; - up->port.ops = &serial8250_pops; - } -} - -static void __init serial8250_register_ports(struct uart_driver *drv) -{ - int i; - - serial8250_isa_init_ports(); - - for (i = 0; i < UART_NR; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - up->port.line = i; - up->port.ops = &serial8250_pops; - init_timer(&up->timer); - up->timer.function = serial8250_timeout; - - /* - * ALPHA_KLUDGE_MCR needs to be killed. - */ - up->mcr_mask = ~ALPHA_KLUDGE_MCR; - up->mcr_force = ALPHA_KLUDGE_MCR; - - uart_add_one_port(drv, &up->port); - } -} - -#ifdef CONFIG_SERIAL_AU1X00_CONSOLE - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* - * Wait for transmitter & holding register to empty - */ -static inline void wait_for_xmitr(struct uart_8250_port *up) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = serial_in(up, UART_LSR); - - if (status & UART_LSR_BI) - up->lsr_break_flag = UART_LSR_BI; - - if (--tmout == 0) - break; - udelay(1); - } while ((status & BOTH_EMPTY) != BOTH_EMPTY); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout && - ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) - udelay(1); - } -} - -static void au1x00_console_putchar(struct uart_port *port, int ch) -{ - struct uart_8250_port *up = (struct uart_8250_port *)port; - - wait_for_xmitr(up); - serial_out(up, UART_TX, ch); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console_lock must be held when we get here. - */ -static void -serial8250_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_8250_port *up = &serial8250_ports[co->index]; - unsigned int ier; - - /* - * First save the UER then disable the interrupts - */ - ier = serial_in(up, UART_IER); - serial_out(up, UART_IER, 0); - - uart_console_write(&up->port, s, count, au1x00_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - serial_out(up, UART_IER, ier); -} - -static int __init serial8250_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= UART_NR) - co->index = 0; - port = &serial8250_ports[co->index].port; - - /* - * Temporary fix. - */ - spin_lock_init(&port->lock); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -extern struct uart_driver serial8250_reg; -static struct console serial8250_console = { - .name = "ttyS", - .write = serial8250_console_write, - .device = uart_console_device, - .setup = serial8250_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &serial8250_reg, -}; - -static int __init serial8250_console_init(void) -{ - serial8250_isa_init_ports(); - register_console(&serial8250_console); - return 0; -} -console_initcall(serial8250_console_init); - -#define SERIAL8250_CONSOLE &serial8250_console -#else -#define SERIAL8250_CONSOLE NULL -#endif - -static struct uart_driver serial8250_reg = { - .owner = THIS_MODULE, - .driver_name = "serial", - .devfs_name = "tts/", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = UART_NR, - .cons = SERIAL8250_CONSOLE, -}; - -int __init early_serial_setup(struct uart_port *port) -{ - serial8250_isa_init_ports(); - serial8250_ports[port->line].port = *port; - serial8250_ports[port->line].port.ops = &serial8250_pops; - return 0; -} - -/** - * serial8250_suspend_port - suspend one serial port - * @line: serial line number - * @level: the level of port suspension, as per uart_suspend_port - * - * Suspend one serial port. - */ -void serial8250_suspend_port(int line) -{ - uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port); -} - -/** - * serial8250_resume_port - resume one serial port - * @line: serial line number - * @level: the level of port resumption, as per uart_resume_port - * - * Resume one serial port. - */ -void serial8250_resume_port(int line) -{ - uart_resume_port(&serial8250_reg, &serial8250_ports[line].port); -} - -static int __init serial8250_init(void) -{ - int ret, i; - - printk(KERN_INFO "Serial: Au1x00 driver\n"); - - for (i = 0; i < NR_IRQS; i++) - spin_lock_init(&irq_lists[i].lock); - - ret = uart_register_driver(&serial8250_reg); - if (ret >= 0) - serial8250_register_ports(&serial8250_reg); - - return ret; -} - -static void __exit serial8250_exit(void) -{ - int i; - - for (i = 0; i < UART_NR; i++) - uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port); - - uart_unregister_driver(&serial8250_reg); -} - -module_init(serial8250_init); -module_exit(serial8250_exit); - -EXPORT_SYMBOL(serial8250_suspend_port); -EXPORT_SYMBOL(serial8250_resume_port); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Au1x00 serial driver\n"); diff --git a/include/asm-mips/serial.h b/include/asm-mips/serial.h index e796d75f027e..7b2366412203 100644 --- a/include/asm-mips/serial.h +++ b/include/asm-mips/serial.h @@ -103,88 +103,6 @@ #define IVR_SERIAL_PORT_DEFNS #endif -#ifdef CONFIG_SERIAL_AU1X00 -#include -#ifdef CONFIG_SOC_AU1000 -#define AU1000_SERIAL_PORT_DEFNS \ - { .baud_base = 0, .port = UART0_ADDR, \ - .iomem_base = (unsigned char *)UART0_ADDR, \ - .irq = AU1000_UART0_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, \ - { .baud_base = 0, .port = UART1_ADDR, \ - .iomem_base = (unsigned char *)UART1_ADDR, \ - .irq = AU1000_UART1_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, \ - { .baud_base = 0, .port = UART2_ADDR, \ - .iomem_base = (unsigned char *)UART2_ADDR, \ - .irq = AU1000_UART2_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, \ - { .baud_base = 0, .port = UART3_ADDR, \ - .iomem_base = (unsigned char *)UART3_ADDR, \ - .irq = AU1000_UART3_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, -#endif - -#ifdef CONFIG_SOC_AU1500 -#define AU1000_SERIAL_PORT_DEFNS \ - { .baud_base = 0, .port = UART0_ADDR, \ - .iomem_base = (unsigned char *)UART0_ADDR, \ - .irq = AU1500_UART0_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, \ - { .baud_base = 0, .port = UART3_ADDR, \ - .iomem_base = (unsigned char *)UART3_ADDR, \ - .irq = AU1500_UART3_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, -#endif - -#ifdef CONFIG_SOC_AU1100 -#define AU1000_SERIAL_PORT_DEFNS \ - { .baud_base = 0, .port = UART0_ADDR, \ - .iomem_base = (unsigned char *)UART0_ADDR, \ - .irq = AU1100_UART0_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, \ - { .baud_base = 0, .port = UART1_ADDR, \ - .iomem_base = (unsigned char *)UART1_ADDR, \ - .irq = AU1100_UART1_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, \ - { .baud_base = 0, .port = UART3_ADDR, \ - .iomem_base = (unsigned char *)UART3_ADDR, \ - .irq = AU1100_UART3_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, -#endif - -#ifdef CONFIG_SOC_AU1550 -#define AU1000_SERIAL_PORT_DEFNS \ - { .baud_base = 0, .port = UART0_ADDR, \ - .iomem_base = (unsigned char *)UART0_ADDR, \ - .irq = AU1550_UART0_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, \ - { .baud_base = 0, .port = UART1_ADDR, \ - .iomem_base = (unsigned char *)UART1_ADDR, \ - .irq = AU1550_UART1_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, \ - { .baud_base = 0, .port = UART3_ADDR, \ - .iomem_base = (unsigned char *)UART3_ADDR, \ - .irq = AU1550_UART3_INT, .flags = STD_COM_FLAGS,\ - .iomem_reg_shift = 2 }, -#endif - -#ifdef CONFIG_SOC_AU1200 -#define AU1000_SERIAL_PORT_DEFNS \ - { .baud_base = 0, .port = UART0_ADDR, \ - .iomem_base = (unsigned char *)UART0_ADDR, \ - .irq = AU1200_UART0_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, \ - { .baud_base = 0, .port = UART1_ADDR, \ - .iomem_base = (unsigned char *)UART1_ADDR, \ - .irq = AU1200_UART1_INT, .flags = STD_COM_FLAGS, \ - .iomem_reg_shift = 2 }, -#endif - -#else -#define AU1000_SERIAL_PORT_DEFNS -#endif - #ifdef CONFIG_HAVE_STD_PC_SERIAL_PORT #define STD_SERIAL_PORT_DEFNS \ /* UART CLK PORT IRQ FLAGS */ \ @@ -331,7 +249,6 @@ MOMENCO_OCELOT_G_SERIAL_PORT_DEFNS \ MOMENCO_OCELOT_C_SERIAL_PORT_DEFNS \ MOMENCO_OCELOT_SERIAL_PORT_DEFNS \ - MOMENCO_OCELOT_3_SERIAL_PORT_DEFNS \ - AU1000_SERIAL_PORT_DEFNS + MOMENCO_OCELOT_3_SERIAL_PORT_DEFNS #endif /* _ASM_SERIAL_H */ -- cgit v1.2.3 From c9b4470e2d9d611181fe488fdf463948d8b24901 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Sun, 26 Mar 2006 21:40:27 +0100 Subject: [ARM] 3414/1: ep93xx: reset ethernet controller before uncompressing Patch from Lennert Buytenhek The Redboot version that cirrus supplies for the cirrus ep93xx doesn't turn off DMA from the ethernet MAC before jumping to linux, which means that we might end up with bits of RX status and packet data scribbled over the uncompressed kernel image. Work around this by resetting the ethernet MAC before we uncompress. We don't usually work around bootloader bugs, but considering that the large majority of ep93xx boards out there have this problem, I figured this it was justified in this case. Signed-off-by: Lennert Buytenhek Signed-off-by: Russell King --- include/asm-arm/arch-ep93xx/uncompress.h | 40 +++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-arm/arch-ep93xx/uncompress.h b/include/asm-arm/arch-ep93xx/uncompress.h index 4410d217077e..2171082d4fc5 100644 --- a/include/asm-arm/arch-ep93xx/uncompress.h +++ b/include/asm-arm/arch-ep93xx/uncompress.h @@ -16,11 +16,21 @@ static unsigned char __raw_readb(unsigned int ptr) return *((volatile unsigned char *)ptr); } +static unsigned int __raw_readl(unsigned int ptr) +{ + return *((volatile unsigned int *)ptr); +} + static void __raw_writeb(unsigned char value, unsigned int ptr) { *((volatile unsigned char *)ptr) = value; } +static void __raw_writel(unsigned int value, unsigned int ptr) +{ + *((volatile unsigned int *)ptr) = value; +} + #define PHYS_UART1_DATA 0x808c0000 #define PHYS_UART1_FLAG 0x808c0018 @@ -49,5 +59,33 @@ static void putstr(const char *s) } } -#define arch_decomp_setup() + +/* + * Some bootloaders don't turn off DMA from the ethernet MAC before + * jumping to linux, which means that we might end up with bits of RX + * status and packet data scribbled over the uncompressed kernel image. + * Work around this by resetting the ethernet MAC before we uncompress. + */ +#define PHYS_ETH_SELF_CTL 0x80010020 +#define ETH_SELF_CTL_RESET 0x00000001 + +static void ethernet_reset(void) +{ + unsigned int v; + + /* Reset the ethernet MAC. */ + v = __raw_readl(PHYS_ETH_SELF_CTL); + __raw_writel(v | ETH_SELF_CTL_RESET, PHYS_ETH_SELF_CTL); + + /* Wait for reset to finish. */ + while (__raw_readl(PHYS_ETH_SELF_CTL) & ETH_SELF_CTL_RESET) + ; +} + + +static void arch_decomp_setup(void) +{ + ethernet_reset(); +} + #define arch_decomp_wdog() -- cgit v1.2.3 From fbb18a277a6f192404aa20ece49529acb1e1e76d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 26 Mar 2006 23:13:39 +0100 Subject: [SERIAL] amba-pl010: allow platforms to specify modem control method The amba-pl010 hardware does not provide RTS and DTR control lines; it is expected that these will be implemented using GPIO. Allow platforms to supply a function to implement manipulation of modem control lines. Signed-off-by: Russell King --- arch/arm/mach-integrator/core.c | 46 ++++++++++++ drivers/serial/amba-pl010.c | 160 +++++++++++++++++----------------------- include/linux/amba/serial.h | 6 ++ 3 files changed, 119 insertions(+), 93 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c index 20071a2767cc..576a5e979c00 100644 --- a/arch/arm/mach-integrator/core.c +++ b/arch/arm/mach-integrator/core.c @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include #include @@ -28,6 +30,8 @@ #include "common.h" +static struct amba_pl010_data integrator_uart_data; + static struct amba_device rtc_device = { .dev = { .bus_id = "mb:15", @@ -44,6 +48,7 @@ static struct amba_device rtc_device = { static struct amba_device uart0_device = { .dev = { .bus_id = "mb:16", + .platform_data = &integrator_uart_data, }, .res = { .start = INTEGRATOR_UART0_BASE, @@ -57,6 +62,7 @@ static struct amba_device uart0_device = { static struct amba_device uart1_device = { .dev = { .bus_id = "mb:17", + .platform_data = &integrator_uart_data, }, .res = { .start = INTEGRATOR_UART1_BASE, @@ -115,6 +121,46 @@ static int __init integrator_init(void) arch_initcall(integrator_init); +/* + * On the Integrator platform, the port RTS and DTR are provided by + * bits in the following SC_CTRLS register bits: + * RTS DTR + * UART0 7 6 + * UART1 5 4 + */ +#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) +#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) + +static void integrator_uart_set_mctrl(struct amba_device *dev, void __iomem *base, unsigned int mctrl) +{ + unsigned int ctrls = 0, ctrlc = 0, rts_mask, dtr_mask; + + if (dev == &uart0_device) { + rts_mask = 1 << 4; + dtr_mask = 1 << 5; + } else { + rts_mask = 1 << 6; + dtr_mask = 1 << 7; + } + + if (mctrl & TIOCM_RTS) + ctrlc |= rts_mask; + else + ctrls |= rts_mask; + + if (mctrl & TIOCM_DTR) + ctrlc |= dtr_mask; + else + ctrls |= dtr_mask; + + __raw_writel(ctrls, SC_CTRLS); + __raw_writel(ctrlc, SC_CTRLC); +} + +static struct amba_pl010_data integrator_uart_data = { + .set_mctrl = integrator_uart_set_mctrl, +}; + #define CM_CTRL IO_ADDRESS(INTEGRATOR_HDR_BASE) + INTEGRATOR_HDR_CTRL_OFFSET static DEFINE_SPINLOCK(cm_lock); diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index 127d6cd5de7f..1631414000a2 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -51,8 +51,6 @@ #include #include -#include -#include #define UART_NR 2 @@ -65,26 +63,16 @@ #define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0) #define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0) -#define UART_DUMMY_RSR_RX /*256*/0 +#define UART_DUMMY_RSR_RX 256 #define UART_PORT_SIZE 64 -/* - * On the Integrator platform, the port RTS and DTR are provided by - * bits in the following SC_CTRLS register bits: - * RTS DTR - * UART0 7 6 - * UART1 5 4 - */ -#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) -#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) - /* * We wrap our port structure around the generic uart_port. */ struct uart_amba_port { struct uart_port port; - unsigned int dtr_mask; - unsigned int rts_mask; + struct amba_device *dev; + struct amba_pl010_data *data; unsigned int old_status; }; @@ -300,20 +288,9 @@ static unsigned int pl010_get_mctrl(struct uart_port *port) static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int ctrls = 0, ctrlc = 0; - - if (mctrl & TIOCM_RTS) - ctrlc |= uap->rts_mask; - else - ctrls |= uap->rts_mask; - if (mctrl & TIOCM_DTR) - ctrlc |= uap->dtr_mask; - else - ctrls |= uap->dtr_mask; - - __raw_writel(ctrls, SC_CTRLS); - __raw_writel(ctrlc, SC_CTRLC); + if (uap->data) + uap->data->set_mctrl(uap->dev, uap->port.membase, mctrl); } static void pl010_break_ctl(struct uart_port *port, int break_state) @@ -539,38 +516,7 @@ static struct uart_ops amba_pl010_pops = { .verify_port = pl010_verify_port, }; -static struct uart_amba_port amba_ports[UART_NR] = { - { - .port = { - .membase = (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE), - .mapbase = INTEGRATOR_UART0_BASE, - .iotype = UPIO_MEM, - .irq = IRQ_UARTINT0, - .uartclk = 14745600, - .fifosize = 16, - .ops = &amba_pl010_pops, - .flags = UPF_BOOT_AUTOCONF, - .line = 0, - }, - .dtr_mask = 1 << 5, - .rts_mask = 1 << 4, - }, - { - .port = { - .membase = (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE), - .mapbase = INTEGRATOR_UART1_BASE, - .iotype = UPIO_MEM, - .irq = IRQ_UARTINT1, - .uartclk = 14745600, - .fifosize = 16, - .ops = &amba_pl010_pops, - .flags = UPF_BOOT_AUTOCONF, - .line = 1, - }, - .dtr_mask = 1 << 7, - .rts_mask = 1 << 6, - } -}; +static struct uart_amba_port *amba_ports[UART_NR]; #ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE @@ -588,7 +534,7 @@ static void pl010_console_putchar(struct uart_port *port, int ch) static void pl010_console_write(struct console *co, const char *s, unsigned int count) { - struct uart_port *port = &amba_ports[co->index].port; + struct uart_port *port = &amba_ports[co->index]->port; unsigned int status, old_cr; /* @@ -651,7 +597,7 @@ static int __init pl010_console_setup(struct console *co, char *options) */ if (co->index >= UART_NR) co->index = 0; - port = &amba_ports[co->index].port; + port = &amba_ports[co->index]->port; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -672,24 +618,6 @@ static struct console amba_console = { .data = &amba_reg, }; -static int __init amba_console_init(void) -{ - /* - * All port initializations are done statically - */ - register_console(&amba_console); - return 0; -} -console_initcall(amba_console_init); - -static int __init amba_late_console_init(void) -{ - if (!(amba_console.flags & CON_ENABLED)) - register_console(&amba_console); - return 0; -} -late_initcall(amba_late_console_init); - #define AMBA_CONSOLE &amba_console #else #define AMBA_CONSOLE NULL @@ -707,30 +635,76 @@ static struct uart_driver amba_reg = { static int pl010_probe(struct amba_device *dev, void *id) { - int i; + struct uart_amba_port *port; + void __iomem *base; + int i, ret; - for (i = 0; i < UART_NR; i++) { - if (amba_ports[i].port.mapbase != dev->res.start) - continue; + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) + if (amba_ports[i] == NULL) + break; - amba_ports[i].port.dev = &dev->dev; - uart_add_one_port(&amba_reg, &amba_ports[i].port); - amba_set_drvdata(dev, &amba_ports[i]); - break; + if (i == ARRAY_SIZE(amba_ports)) { + ret = -EBUSY; + goto out; } - return 0; + port = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL); + if (!port) { + ret = -ENOMEM; + goto out; + } + + base = ioremap(dev->res.start, PAGE_SIZE); + if (!base) { + ret = -ENOMEM; + goto free; + } + + port->port.dev = &dev->dev; + port->port.mapbase = dev->res.start; + port->port.membase = base; + port->port.iotype = UPIO_MEM; + port->port.irq = dev->irq[0]; + port->port.uartclk = 14745600; + port->port.fifosize = 16; + port->port.ops = &amba_pl010_pops; + port->port.flags = UPF_BOOT_AUTOCONF; + port->port.line = i; + port->dev = dev; + port->data = dev->dev.platform_data; + + amba_ports[i] = port; + + amba_set_drvdata(dev, port); + ret = uart_add_one_port(&amba_reg, &port->port); + if (ret) { + amba_set_drvdata(dev, NULL); + amba_ports[i] = NULL; + iounmap(base); + free: + kfree(port); + } + + out: + return ret; } static int pl010_remove(struct amba_device *dev) { - struct uart_amba_port *uap = amba_get_drvdata(dev); - - if (uap) - uart_remove_one_port(&amba_reg, &uap->port); + struct uart_amba_port *port = amba_get_drvdata(dev); + int i; amba_set_drvdata(dev, NULL); + uart_remove_one_port(&amba_reg, &port->port); + + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) + if (amba_ports[i] == port) + amba_ports[i] = NULL; + + iounmap(port->port.membase); + kfree(port); + return 0; } diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index dc726ffccebd..48ee32a18ac5 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -158,4 +158,10 @@ #define UART01x_RSR_ANY (UART01x_RSR_OE|UART01x_RSR_BE|UART01x_RSR_PE|UART01x_RSR_FE) #define UART01x_FR_MODEM_ANY (UART01x_FR_DCD|UART01x_FR_DSR|UART01x_FR_CTS) +#ifndef __ASSEMBLY__ +struct amba_pl010_data { + void (*set_mctrl)(struct amba_device *dev, void __iomem *base, unsigned int mctrl); +}; +#endif + #endif -- cgit v1.2.3 From 3eb4801d7bde42b82f05137392a1ee0ece090bad Mon Sep 17 00:00:00 2001 From: Norbert Kiesel Date: Sun, 26 Mar 2006 17:39:55 -0800 Subject: [NET]: drop duplicate assignment in request_sock Just noticed that request_sock.[ch] contain a useless assignment of rskq_accept_head to itself. I assume this is a typo and the 2nd one was supposed to be _tail. However, setting _tail to NULL is not needed, so the patch below just drops the 2nd assignment. Signed-off-By: Norbert Kiesel Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller --- include/net/request_sock.h | 2 +- net/core/request_sock.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 11641c9384f7..c5d7f920c352 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -145,7 +145,7 @@ static inline struct request_sock * { struct request_sock *req = queue->rskq_accept_head; - queue->rskq_accept_head = queue->rskq_accept_head = NULL; + queue->rskq_accept_head = NULL; return req; } diff --git a/net/core/request_sock.c b/net/core/request_sock.c index 98f0fc923f91..1e44eda1fda9 100644 --- a/net/core/request_sock.c +++ b/net/core/request_sock.c @@ -51,7 +51,7 @@ int reqsk_queue_alloc(struct request_sock_queue *queue, get_random_bytes(&lopt->hash_rnd, sizeof(lopt->hash_rnd)); rwlock_init(&queue->syn_wait_lock); - queue->rskq_accept_head = queue->rskq_accept_head = NULL; + queue->rskq_accept_head = NULL; lopt->nr_table_entries = nr_table_entries; write_lock_bh(&queue->syn_wait_lock); -- cgit v1.2.3 From 7c92943c7b6c42fa631ac2b67aeb507e727cd75b Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 23 Mar 2006 17:36:59 +1100 Subject: [PATCH] powerpc: work around sparse warnings in cputable.h Christoph noticed that sparse warned about all the enum tags in cuptable.h that had values that required them to be type log. (enum tags are ints according to the standard.) This patch attempts to fix them in the least intrusive way possible by turning them all into #defines except for the 32 bit CPU_FTRS_POSSIBLE and CPU_FTRS_ALWAYS which are hard to construct that way. This works because these last two contain no bits above 2^31. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras --- include/asm-powerpc/cputable.h | 299 +++++++++++++++++++++-------------------- 1 file changed, 152 insertions(+), 147 deletions(-) (limited to 'include') diff --git a/include/asm-powerpc/cputable.h b/include/asm-powerpc/cputable.h index fe45f6f3a4be..4321483cce51 100644 --- a/include/asm-powerpc/cputable.h +++ b/include/asm-powerpc/cputable.h @@ -188,153 +188,154 @@ extern void do_cpu_ftr_fixups(unsigned long offset); !defined(CONFIG_POWER3) && !defined(CONFIG_POWER4) && \ !defined(CONFIG_BOOKE)) -enum { - CPU_FTRS_PPC601 = CPU_FTR_COMMON | CPU_FTR_601 | CPU_FTR_HPTE_TABLE, - CPU_FTRS_603 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | - CPU_FTR_MAYBE_CAN_NAP, - CPU_FTRS_604 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON | CPU_FTR_HPTE_TABLE, - CPU_FTRS_740_NOTAU = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | - CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, - CPU_FTRS_740 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, - CPU_FTRS_750 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, - CPU_FTRS_750FX1 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | - CPU_FTR_DUAL_PLL_750FX | CPU_FTR_NO_DPM, - CPU_FTRS_750FX2 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | - CPU_FTR_NO_DPM, - CPU_FTRS_750FX = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | - CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS, - CPU_FTRS_750GX = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | - CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | - CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | - CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS, - CPU_FTRS_7400_NOTAU = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | - CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | - CPU_FTR_MAYBE_CAN_NAP, - CPU_FTRS_7400 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | - CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | - CPU_FTR_MAYBE_CAN_NAP, - CPU_FTRS_7450_20 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NEED_COHERENT, - CPU_FTRS_7450_21 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | - CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | - CPU_FTR_NEED_COHERENT, - CPU_FTRS_7450_23 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | - CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_NEED_COHERENT, - CPU_FTRS_7455_1 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | - CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | - CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_HAS_HIGH_BATS | - CPU_FTR_NEED_COHERENT, - CPU_FTRS_7455_20 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | - CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | - CPU_FTR_NEED_COHERENT | CPU_FTR_HAS_HIGH_BATS, - CPU_FTRS_7455 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | - CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | - CPU_FTR_NEED_COHERENT, - CPU_FTRS_7447_10 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | - CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | - CPU_FTR_NEED_COHERENT | CPU_FTR_NO_BTIC, - CPU_FTRS_7447 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | - CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | - CPU_FTR_NEED_COHERENT, - CPU_FTRS_7447A = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | - CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | - CPU_FTR_NEED_COHERENT, - CPU_FTRS_82XX = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB, - CPU_FTRS_G2_LE = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | - CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS, - CPU_FTRS_E300 = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | - CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS | - CPU_FTR_COMMON, - CPU_FTRS_CLASSIC32 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE, - CPU_FTRS_POWER3_32 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE, - CPU_FTRS_POWER4_32 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_NODSISRALIGN, - CPU_FTRS_970_32 = CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | - CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN, - CPU_FTRS_8XX = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB, - CPU_FTRS_40X = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_NODSISRALIGN, - CPU_FTRS_44X = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_NODSISRALIGN, - CPU_FTRS_E200 = CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN, - CPU_FTRS_E500 = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_NODSISRALIGN, - CPU_FTRS_E500_2 = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_BIG_PHYS | CPU_FTR_NODSISRALIGN, - CPU_FTRS_GENERIC_32 = CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN, +#define CPU_FTRS_PPC601 (CPU_FTR_COMMON | CPU_FTR_601 | CPU_FTR_HPTE_TABLE) +#define CPU_FTRS_603 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \ + CPU_FTR_MAYBE_CAN_NAP) +#define CPU_FTRS_604 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON | CPU_FTR_HPTE_TABLE) +#define CPU_FTRS_740_NOTAU (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP) +#define CPU_FTRS_740 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP) +#define CPU_FTRS_750 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP) +#define CPU_FTRS_750FX1 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ + CPU_FTR_DUAL_PLL_750FX | CPU_FTR_NO_DPM) +#define CPU_FTRS_750FX2 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ + CPU_FTR_NO_DPM) +#define CPU_FTRS_750FX (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ + CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS) +#define CPU_FTRS_750GX (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ + CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS) +#define CPU_FTRS_7400_NOTAU (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | \ + CPU_FTR_MAYBE_CAN_NAP) +#define CPU_FTRS_7400 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ + CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | \ + CPU_FTR_MAYBE_CAN_NAP) +#define CPU_FTRS_7450_20 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ + CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ + CPU_FTR_NEED_COHERENT) +#define CPU_FTRS_7450_21 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | \ + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ + CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | \ + CPU_FTR_NEED_COHERENT) +#define CPU_FTRS_7450_23 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | \ + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ + CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_NEED_COHERENT) +#define CPU_FTRS_7455_1 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | \ + CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_HAS_HIGH_BATS | \ + CPU_FTR_NEED_COHERENT) +#define CPU_FTRS_7455_20 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | \ + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ + CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | \ + CPU_FTR_NEED_COHERENT | CPU_FTR_HAS_HIGH_BATS) +#define CPU_FTRS_7455 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | \ + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ + CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ + CPU_FTR_NEED_COHERENT) +#define CPU_FTRS_7447_10 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | \ + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ + CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ + CPU_FTR_NEED_COHERENT | CPU_FTR_NO_BTIC) +#define CPU_FTRS_7447 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | \ + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ + CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ + CPU_FTR_NEED_COHERENT) +#define CPU_FTRS_7447A (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | \ + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ + CPU_FTR_NEED_COHERENT) +#define CPU_FTRS_82XX (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB) +#define CPU_FTRS_G2_LE (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ + CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS) +#define CPU_FTRS_E300 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ + CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS | \ + CPU_FTR_COMMON) +#define CPU_FTRS_CLASSIC32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE) +#define CPU_FTRS_POWER3_32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE) +#define CPU_FTRS_POWER4_32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_NODSISRALIGN) +#define CPU_FTRS_970_32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ + CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_ALTIVEC_COMP | \ + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN) +#define CPU_FTRS_8XX (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB) +#define CPU_FTRS_40X (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_NODSISRALIGN) +#define CPU_FTRS_44X (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_NODSISRALIGN) +#define CPU_FTRS_E200 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN) +#define CPU_FTRS_E500 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_NODSISRALIGN) +#define CPU_FTRS_E500_2 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_BIG_PHYS | CPU_FTR_NODSISRALIGN) +#define CPU_FTRS_GENERIC_32 (CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN) #ifdef __powerpc64__ - CPU_FTRS_POWER3 = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_HPTE_TABLE | CPU_FTR_IABR, - CPU_FTRS_RS64 = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_HPTE_TABLE | CPU_FTR_IABR | - CPU_FTR_MMCRA | CPU_FTR_CTRL, - CPU_FTRS_POWER4 = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_MMCRA, - CPU_FTRS_PPC970 = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | - CPU_FTR_ALTIVEC_COMP | CPU_FTR_CAN_NAP | CPU_FTR_MMCRA, - CPU_FTRS_POWER5 = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | - CPU_FTR_MMCRA | CPU_FTR_SMT | - CPU_FTR_COHERENT_ICACHE | CPU_FTR_LOCKLESS_TLBIE | - CPU_FTR_MMCRA_SIHV | CPU_FTR_PURR, - CPU_FTRS_CELL = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | - CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | - CPU_FTR_CTRL | CPU_FTR_PAUSE_ZERO, - CPU_FTRS_COMPATIBLE = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | - CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2, +#define CPU_FTRS_POWER3 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_IABR) +#define CPU_FTRS_RS64 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_IABR | \ + CPU_FTR_MMCRA | CPU_FTR_CTRL) +#define CPU_FTRS_POWER4 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_MMCRA) +#define CPU_FTRS_PPC970 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \ + CPU_FTR_ALTIVEC_COMP | CPU_FTR_CAN_NAP | CPU_FTR_MMCRA) +#define CPU_FTRS_POWER5 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \ + CPU_FTR_MMCRA | CPU_FTR_SMT | \ + CPU_FTR_COHERENT_ICACHE | CPU_FTR_LOCKLESS_TLBIE | \ + CPU_FTR_MMCRA_SIHV | CPU_FTR_PURR) +#define CPU_FTRS_CELL (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \ + CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ + CPU_FTR_CTRL | CPU_FTR_PAUSE_ZERO) +#define CPU_FTRS_COMPATIBLE (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ + CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2) #endif - CPU_FTRS_POSSIBLE = #ifdef __powerpc64__ - CPU_FTRS_POWER3 | CPU_FTRS_RS64 | CPU_FTRS_POWER4 | - CPU_FTRS_PPC970 | CPU_FTRS_POWER5 | CPU_FTRS_CELL | - CPU_FTR_CI_LARGE_PAGE | +#define CPU_FTRS_POSSIBLE \ + (CPU_FTRS_POWER3 | CPU_FTRS_RS64 | CPU_FTRS_POWER4 | \ + CPU_FTRS_PPC970 | CPU_FTRS_POWER5 | CPU_FTRS_CELL | \ + CPU_FTR_CI_LARGE_PAGE) #else +enum { + CPU_FTRS_POSSIBLE = #if CLASSIC_PPC CPU_FTRS_PPC601 | CPU_FTRS_603 | CPU_FTRS_604 | CPU_FTRS_740_NOTAU | CPU_FTRS_740 | CPU_FTRS_750 | CPU_FTRS_750FX1 | @@ -368,14 +369,18 @@ enum { #ifdef CONFIG_E500 CPU_FTRS_E500 | CPU_FTRS_E500_2 | #endif -#endif /* __powerpc64__ */ 0, +}; +#endif /* __powerpc64__ */ - CPU_FTRS_ALWAYS = #ifdef __powerpc64__ - CPU_FTRS_POWER3 & CPU_FTRS_RS64 & CPU_FTRS_POWER4 & - CPU_FTRS_PPC970 & CPU_FTRS_POWER5 & CPU_FTRS_CELL & +#define CPU_FTRS_ALWAYS \ + (CPU_FTRS_POWER3 & CPU_FTRS_RS64 & CPU_FTRS_POWER4 & \ + CPU_FTRS_PPC970 & CPU_FTRS_POWER5 & CPU_FTRS_CELL & \ + CPU_FTRS_POSSIBLE) #else +enum { + CPU_FTRS_ALWAYS = #if CLASSIC_PPC CPU_FTRS_PPC601 & CPU_FTRS_603 & CPU_FTRS_604 & CPU_FTRS_740_NOTAU & CPU_FTRS_740 & CPU_FTRS_750 & CPU_FTRS_750FX1 & @@ -409,9 +414,9 @@ enum { #ifdef CONFIG_E500 CPU_FTRS_E500 & CPU_FTRS_E500_2 & #endif -#endif /* __powerpc64__ */ CPU_FTRS_POSSIBLE, }; +#endif /* __powerpc64__ */ static inline int cpu_has_feature(unsigned long feature) { -- cgit v1.2.3 From e3f94b85f98a346c5eb0ac0d9539b71cb7057143 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 23 Mar 2006 23:32:24 +1100 Subject: [PATCH] powerpc: Make BUG_ON & WARN_ON play nice with compile-time optimisations Change BUG_ON and WARN_ON to give the compiler a chance to perform compile-time optimsations. Depending on the complexity of the condition, the compiler may not do this very well, so if it's important check the object code. Current GCC's (4.x) produce good code as long as the condition does not include a function call, including a static inline. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- include/asm-powerpc/bug.h | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-powerpc/bug.h b/include/asm-powerpc/bug.h index 99817a802ca4..8003997ddc73 100644 --- a/include/asm-powerpc/bug.h +++ b/include/asm-powerpc/bug.h @@ -30,6 +30,12 @@ struct bug_entry *find_bug(unsigned long bugaddr); #ifdef CONFIG_BUG +/* + * BUG_ON() and WARN_ON() do their best to cooperate with compile-time + * optimisations. However depending on the complexity of the condition + * some compiler versions may not produce optimal results. + */ + #define BUG() do { \ __asm__ __volatile__( \ "1: twi 31,0,0\n" \ @@ -40,17 +46,36 @@ struct bug_entry *find_bug(unsigned long bugaddr); } while (0) #define BUG_ON(x) do { \ - __asm__ __volatile__( \ + if (__builtin_constant_p(x)) { \ + if (x) \ + BUG(); \ + } else { \ + __asm__ __volatile__( \ "1: "PPC_TLNEI" %0,0\n" \ ".section __bug_table,\"a\"\n" \ "\t"PPC_LONG" 1b,%1,%2,%3\n" \ ".previous" \ : : "r" ((long)(x)), "i" (__LINE__), \ "i" (__FILE__), "i" (__FUNCTION__)); \ + } \ } while (0) -#define WARN_ON(x) do { \ +#define WARN() do { \ __asm__ __volatile__( \ + "1: twi 31,0,0\n" \ + ".section __bug_table,\"a\"\n" \ + "\t"PPC_LONG" 1b,%0,%1,%2\n" \ + ".previous" \ + : : "i" (__LINE__ + BUG_WARNING_TRAP), \ + "i" (__FILE__), "i" (__FUNCTION__)); \ +} while (0) + +#define WARN_ON(x) do { \ + if (__builtin_constant_p(x)) { \ + if (x) \ + WARN(); \ + } else { \ + __asm__ __volatile__( \ "1: "PPC_TLNEI" %0,0\n" \ ".section __bug_table,\"a\"\n" \ "\t"PPC_LONG" 1b,%1,%2,%3\n" \ @@ -58,6 +83,7 @@ struct bug_entry *find_bug(unsigned long bugaddr); : : "r" ((long)(x)), \ "i" (__LINE__ + BUG_WARNING_TRAP), \ "i" (__FILE__), "i" (__FUNCTION__)); \ + } \ } while (0) #define HAVE_ARCH_BUG -- cgit v1.2.3 From dd4d7bfad635dddc56b74dab1894ef01c8c836e1 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 23 Mar 2006 23:33:03 +1100 Subject: [PATCH] powerpc: Change firmware_has_feature() to a macro So that we can use firmware_has_feature() in a BUG_ON() and have the compiler elide the code entirely if the feature can never be set, change firmware_has_feature to a macro. Unfortunate, but necessary at least until GCC bug #26724 is fixed. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- include/asm-powerpc/firmware.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/asm-powerpc/firmware.h b/include/asm-powerpc/firmware.h index ce3788224ed0..03c2fdff021b 100644 --- a/include/asm-powerpc/firmware.h +++ b/include/asm-powerpc/firmware.h @@ -84,11 +84,9 @@ enum { */ extern unsigned long ppc64_firmware_features; -static inline unsigned long firmware_has_feature(unsigned long feature) -{ - return (FW_FEATURE_ALWAYS & feature) || - (FW_FEATURE_POSSIBLE & ppc64_firmware_features & feature); -} +#define firmware_has_feature(feature) \ + ((FW_FEATURE_ALWAYS & (feature)) || \ + (FW_FEATURE_POSSIBLE & ppc64_firmware_features & (feature))) extern void system_reset_fwnmi(void); extern void machine_check_fwnmi(void); -- cgit v1.2.3 From a7f31841a40776605c834053ad1eb82d539bd79f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 23 Mar 2006 00:00:08 +0100 Subject: [PATCH] powerpc: declare arch syscalls in powerpc currently declares some of its own system calls in , but not all of them. That place also contains remainders of the now almost unused kernel syscall hack. - Add a new with clean declarations - Include that file from every source that implements one of these - Get rid of old declarations in This patch is required as a base for implementing system calls from an SPU, but also makes sense as a general cleanup. Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/process.c | 1 + arch/powerpc/kernel/rtas.c | 1 + arch/powerpc/kernel/signal_32.c | 1 + arch/powerpc/kernel/signal_64.c | 1 + arch/powerpc/kernel/syscalls.c | 1 + include/asm-powerpc/syscalls.h | 58 +++++++++++++++++++++++++++++++++++++++++ include/asm-powerpc/unistd.h | 35 +------------------------ 7 files changed, 64 insertions(+), 34 deletions(-) create mode 100644 include/asm-powerpc/syscalls.h (limited to 'include') diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 995b14688d3a..c6e81bbd615c 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -45,6 +45,7 @@ #include #include #include +#include #ifdef CONFIG_PPC64 #include #endif diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index b5b2add7ad1e..142d818a31a6 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -32,6 +32,7 @@ #include #include #include +#include struct rtas_t rtas = { .lock = SPIN_LOCK_UNLOCKED diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index d7a4e814974d..01e3c08cb550 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -42,6 +42,7 @@ #include #include +#include #include #include #ifdef CONFIG_PPC64 diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 47f910380a6a..82ec2f3f6713 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #define DEBUG_SIG 0 diff --git a/arch/powerpc/kernel/syscalls.c b/arch/powerpc/kernel/syscalls.c index ad895c99813b..9b69d99a9103 100644 --- a/arch/powerpc/kernel/syscalls.c +++ b/arch/powerpc/kernel/syscalls.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include diff --git a/include/asm-powerpc/syscalls.h b/include/asm-powerpc/syscalls.h new file mode 100644 index 000000000000..c2fe79d4f90f --- /dev/null +++ b/include/asm-powerpc/syscalls.h @@ -0,0 +1,58 @@ +#ifndef __ASM_POWERPC_SYSCALLS_H +#define __ASM_POWERPC_SYSCALLS_H +#ifdef __KERNEL__ + +#include +#include +#include +#include + +struct new_utsname; +struct pt_regs; +struct rtas_args; +struct sigaction; + +asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t offset); +asmlinkage unsigned long sys_mmap2(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff); +asmlinkage int sys_execve(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, struct pt_regs *regs); +asmlinkage int sys_clone(unsigned long clone_flags, unsigned long usp, + int __user *parent_tidp, void __user *child_threadptr, + int __user *child_tidp, int p6, struct pt_regs *regs); +asmlinkage int sys_fork(unsigned long p1, unsigned long p2, + unsigned long p3, unsigned long p4, unsigned long p5, + unsigned long p6, struct pt_regs *regs); +asmlinkage int sys_vfork(unsigned long p1, unsigned long p2, + unsigned long p3, unsigned long p4, unsigned long p5, + unsigned long p6, struct pt_regs *regs); +asmlinkage int sys_pipe(int __user *fildes); +asmlinkage long sys_rt_sigaction(int sig, + const struct sigaction __user *act, + struct sigaction __user *oact, size_t sigsetsize); +asmlinkage int sys_ipc(uint call, int first, unsigned long second, + long third, void __user *ptr, long fifth); +asmlinkage long ppc64_personality(unsigned long personality); +asmlinkage int ppc_rtas(struct rtas_args __user *uargs); +asmlinkage time_t sys64_time(time_t __user * tloc); +asmlinkage long ppc_newuname(struct new_utsname __user * name); + +asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, + size_t sigsetsize); + +#ifndef __powerpc64__ +asmlinkage long sys_sigaltstack(const stack_t __user *uss, + stack_t __user *uoss, int r5, int r6, int r7, int r8, + struct pt_regs *regs); +#else /* __powerpc64__ */ +asmlinkage long sys_sigaltstack(const stack_t __user *uss, + stack_t __user *uoss, unsigned long r5, unsigned long r6, + unsigned long r7, unsigned long r8, struct pt_regs *regs); +#endif /* __powerpc64__ */ + +#endif /* __KERNEL__ */ +#endif /* __ASM_POWERPC_SYSCALLS_H */ diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index 35556993f066..1e990747dce7 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -425,6 +425,7 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6 #include #include #include +#include #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR @@ -460,43 +461,9 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6 * System call prototypes. */ #ifdef __KERNEL_SYSCALLS__ -extern pid_t setsid(void); -extern int write(int fd, const char *buf, off_t count); -extern int read(int fd, char *buf, off_t count); -extern off_t lseek(int fd, off_t offset, int count); -extern int dup(int fd); extern int execve(const char *file, char **argv, char **envp); -extern int open(const char *file, int flag, int mode); -extern int close(int fd); -extern pid_t waitpid(pid_t pid, int *wait_stat, int options); #endif /* __KERNEL_SYSCALLS__ */ -/* - * Functions that implement syscalls. - */ -unsigned long sys_mmap(unsigned long addr, size_t len, unsigned long prot, - unsigned long flags, unsigned long fd, off_t offset); -unsigned long sys_mmap2(unsigned long addr, size_t len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long pgoff); -struct pt_regs; -int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, - unsigned long a3, unsigned long a4, unsigned long a5, - struct pt_regs *regs); -int sys_clone(unsigned long clone_flags, unsigned long usp, - int __user *parent_tidp, void __user *child_threadptr, - int __user *child_tidp, int p6, struct pt_regs *regs); -int sys_fork(unsigned long p1, unsigned long p2, unsigned long p3, - unsigned long p4, unsigned long p5, unsigned long p6, - struct pt_regs *regs); -int sys_vfork(unsigned long p1, unsigned long p2, unsigned long p3, - unsigned long p4, unsigned long p5, unsigned long p6, - struct pt_regs *regs); -int sys_pipe(int __user *fildes); -struct sigaction; -long sys_rt_sigaction(int sig, const struct sigaction __user *act, - struct sigaction __user *oact, size_t sigsetsize); - /* * "Conditional" syscalls * -- cgit v1.2.3 From 2dd14934c9138c562d93c501e88c6d6f061eb8ba Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 23 Mar 2006 00:00:09 +0100 Subject: [PATCH] spufs: allow SPU code to do syscalls An SPU does not have a way to implement system calls itself, but it can create intercepts to the kernel. This patch uses the method defined by the JSRE interface for C99 host library calls from an SPU to implement Linux system calls. It uses the reserved SPU stop code 0x2104 for this, using the structure layout and syscall numbers for ppc64-linux. I'm still undecided wether it is better to have a list of allowed syscalls or a list of forbidden syscalls, since we can't allow an SPU to call all syscalls that are defined for ppc64-linux. This patch implements the easier choice of them, with a blacklist that only prevents an SPU from calling anything that interacts with its own execution, e.g fork, execve, clone, vfork, exit, spu_run and spu_create and everything that deals with signals. Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/cell/Makefile | 10 +- arch/powerpc/platforms/cell/spu_callbacks.c | 345 ++++++++++++++++++++++++++++ arch/powerpc/platforms/cell/spufs/run.c | 91 ++++++++ include/asm-powerpc/spu.h | 9 +- 4 files changed, 452 insertions(+), 3 deletions(-) create mode 100644 arch/powerpc/platforms/cell/spu_callbacks.c (limited to 'include') diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile index 3b998a393e3f..e570bad06394 100644 --- a/arch/powerpc/platforms/cell/Makefile +++ b/arch/powerpc/platforms/cell/Makefile @@ -6,5 +6,11 @@ obj-$(CONFIG_SPU_FS) += spu-base.o spufs/ spu-base-y += spu_base.o spu_priv1.o -builtin-spufs-$(CONFIG_SPU_FS) += spu_syscalls.o -obj-y += $(builtin-spufs-m) +# needed only when building loadable spufs.ko +spufs-modular-$(CONFIG_SPU_FS) += spu_syscalls.o +obj-y += $(spufs-modular-m) + +# always needed in kernel +spufs-builtin-$(CONFIG_SPU_FS) += spu_callbacks.o +obj-y += $(spufs-builtin-y) $(spufs-builtin-m) + diff --git a/arch/powerpc/platforms/cell/spu_callbacks.c b/arch/powerpc/platforms/cell/spu_callbacks.c new file mode 100644 index 000000000000..3a4245c926ad --- /dev/null +++ b/arch/powerpc/platforms/cell/spu_callbacks.c @@ -0,0 +1,345 @@ +/* + * System call callback functions for SPUs + */ + +#define DEBUG + +#include +#include +#include + +#include +#include +#include + +/* + * This table defines the system calls that an SPU can call. + * It is currently a subset of the 64 bit powerpc system calls, + * with the exact semantics. + * + * The reasons for disabling some of the system calls are: + * 1. They interact with the way SPU syscalls are handled + * and we can't let them execute ever: + * restart_syscall, exit, for, execve, ptrace, ... + * 2. They are deprecated and replaced by other means: + * uselib, pciconfig_*, sysfs, ... + * 3. They are somewhat interacting with the system in a way + * we don't want an SPU to: + * reboot, init_module, mount, kexec_load + * 4. They are optional and we can't rely on them being + * linked into the kernel. Unfortunately, the cond_syscall + * helper does not work here as it does not add the necessary + * opd symbols: + * mbind, mq_open, ipc, ... + */ + +void *spu_syscall_table[] = { + [__NR_restart_syscall] sys_ni_syscall, /* sys_restart_syscall */ + [__NR_exit] sys_ni_syscall, /* sys_exit */ + [__NR_fork] sys_ni_syscall, /* ppc_fork */ + [__NR_read] sys_read, + [__NR_write] sys_write, + [__NR_open] sys_open, + [__NR_close] sys_close, + [__NR_waitpid] sys_waitpid, + [__NR_creat] sys_creat, + [__NR_link] sys_link, + [__NR_unlink] sys_unlink, + [__NR_execve] sys_ni_syscall, /* sys_execve */ + [__NR_chdir] sys_chdir, + [__NR_time] sys_time, + [__NR_mknod] sys_mknod, + [__NR_chmod] sys_chmod, + [__NR_lchown] sys_lchown, + [__NR_break] sys_ni_syscall, + [__NR_oldstat] sys_ni_syscall, + [__NR_lseek] sys_lseek, + [__NR_getpid] sys_getpid, + [__NR_mount] sys_ni_syscall, /* sys_mount */ + [__NR_umount] sys_ni_syscall, + [__NR_setuid] sys_setuid, + [__NR_getuid] sys_getuid, + [__NR_stime] sys_stime, + [__NR_ptrace] sys_ni_syscall, /* sys_ptrace */ + [__NR_alarm] sys_alarm, + [__NR_oldfstat] sys_ni_syscall, + [__NR_pause] sys_ni_syscall, /* sys_pause */ + [__NR_utime] sys_ni_syscall, /* sys_utime */ + [__NR_stty] sys_ni_syscall, + [__NR_gtty] sys_ni_syscall, + [__NR_access] sys_access, + [__NR_nice] sys_nice, + [__NR_ftime] sys_ni_syscall, + [__NR_sync] sys_sync, + [__NR_kill] sys_kill, + [__NR_rename] sys_rename, + [__NR_mkdir] sys_mkdir, + [__NR_rmdir] sys_rmdir, + [__NR_dup] sys_dup, + [__NR_pipe] sys_pipe, + [__NR_times] sys_times, + [__NR_prof] sys_ni_syscall, + [__NR_brk] sys_brk, + [__NR_setgid] sys_setgid, + [__NR_getgid] sys_getgid, + [__NR_signal] sys_ni_syscall, /* sys_signal */ + [__NR_geteuid] sys_geteuid, + [__NR_getegid] sys_getegid, + [__NR_acct] sys_ni_syscall, /* sys_acct */ + [__NR_umount2] sys_ni_syscall, /* sys_umount */ + [__NR_lock] sys_ni_syscall, + [__NR_ioctl] sys_ioctl, + [__NR_fcntl] sys_fcntl, + [__NR_mpx] sys_ni_syscall, + [__NR_setpgid] sys_setpgid, + [__NR_ulimit] sys_ni_syscall, + [__NR_oldolduname] sys_ni_syscall, + [__NR_umask] sys_umask, + [__NR_chroot] sys_chroot, + [__NR_ustat] sys_ni_syscall, /* sys_ustat */ + [__NR_dup2] sys_dup2, + [__NR_getppid] sys_getppid, + [__NR_getpgrp] sys_getpgrp, + [__NR_setsid] sys_setsid, + [__NR_sigaction] sys_ni_syscall, + [__NR_sgetmask] sys_sgetmask, + [__NR_ssetmask] sys_ssetmask, + [__NR_setreuid] sys_setreuid, + [__NR_setregid] sys_setregid, + [__NR_sigsuspend] sys_ni_syscall, + [__NR_sigpending] sys_ni_syscall, + [__NR_sethostname] sys_sethostname, + [__NR_setrlimit] sys_setrlimit, + [__NR_getrlimit] sys_ni_syscall, + [__NR_getrusage] sys_getrusage, + [__NR_gettimeofday] sys_gettimeofday, + [__NR_settimeofday] sys_settimeofday, + [__NR_getgroups] sys_getgroups, + [__NR_setgroups] sys_setgroups, + [__NR_select] sys_ni_syscall, + [__NR_symlink] sys_symlink, + [__NR_oldlstat] sys_ni_syscall, + [__NR_readlink] sys_readlink, + [__NR_uselib] sys_ni_syscall, /* sys_uselib */ + [__NR_swapon] sys_ni_syscall, /* sys_swapon */ + [__NR_reboot] sys_ni_syscall, /* sys_reboot */ + [__NR_readdir] sys_ni_syscall, + [__NR_mmap] sys_mmap, + [__NR_munmap] sys_munmap, + [__NR_truncate] sys_truncate, + [__NR_ftruncate] sys_ftruncate, + [__NR_fchmod] sys_fchmod, + [__NR_fchown] sys_fchown, + [__NR_getpriority] sys_getpriority, + [__NR_setpriority] sys_setpriority, + [__NR_profil] sys_ni_syscall, + [__NR_statfs] sys_ni_syscall, /* sys_statfs */ + [__NR_fstatfs] sys_ni_syscall, /* sys_fstatfs */ + [__NR_ioperm] sys_ni_syscall, + [__NR_socketcall] sys_socketcall, + [__NR_syslog] sys_syslog, + [__NR_setitimer] sys_setitimer, + [__NR_getitimer] sys_getitimer, + [__NR_stat] sys_newstat, + [__NR_lstat] sys_newlstat, + [__NR_fstat] sys_newfstat, + [__NR_olduname] sys_ni_syscall, + [__NR_iopl] sys_ni_syscall, + [__NR_vhangup] sys_vhangup, + [__NR_idle] sys_ni_syscall, + [__NR_vm86] sys_ni_syscall, + [__NR_wait4] sys_wait4, + [__NR_swapoff] sys_ni_syscall, /* sys_swapoff */ + [__NR_sysinfo] sys_sysinfo, + [__NR_ipc] sys_ni_syscall, /* sys_ipc */ + [__NR_fsync] sys_fsync, + [__NR_sigreturn] sys_ni_syscall, + [__NR_clone] sys_ni_syscall, /* ppc_clone */ + [__NR_setdomainname] sys_setdomainname, + [__NR_uname] ppc_newuname, + [__NR_modify_ldt] sys_ni_syscall, + [__NR_adjtimex] sys_adjtimex, + [__NR_mprotect] sys_mprotect, + [__NR_sigprocmask] sys_ni_syscall, + [__NR_create_module] sys_ni_syscall, + [__NR_init_module] sys_ni_syscall, /* sys_init_module */ + [__NR_delete_module] sys_ni_syscall, /* sys_delete_module */ + [__NR_get_kernel_syms] sys_ni_syscall, + [__NR_quotactl] sys_ni_syscall, /* sys_quotactl */ + [__NR_getpgid] sys_getpgid, + [__NR_fchdir] sys_fchdir, + [__NR_bdflush] sys_bdflush, + [__NR_sysfs] sys_ni_syscall, /* sys_sysfs */ + [__NR_personality] ppc64_personality, + [__NR_afs_syscall] sys_ni_syscall, + [__NR_setfsuid] sys_setfsuid, + [__NR_setfsgid] sys_setfsgid, + [__NR__llseek] sys_llseek, + [__NR_getdents] sys_getdents, + [__NR__newselect] sys_select, + [__NR_flock] sys_flock, + [__NR_msync] sys_msync, + [__NR_readv] sys_readv, + [__NR_writev] sys_writev, + [__NR_getsid] sys_getsid, + [__NR_fdatasync] sys_fdatasync, + [__NR__sysctl] sys_ni_syscall, /* sys_sysctl */ + [__NR_mlock] sys_mlock, + [__NR_munlock] sys_munlock, + [__NR_mlockall] sys_mlockall, + [__NR_munlockall] sys_munlockall, + [__NR_sched_setparam] sys_sched_setparam, + [__NR_sched_getparam] sys_sched_getparam, + [__NR_sched_setscheduler] sys_sched_setscheduler, + [__NR_sched_getscheduler] sys_sched_getscheduler, + [__NR_sched_yield] sys_sched_yield, + [__NR_sched_get_priority_max] sys_sched_get_priority_max, + [__NR_sched_get_priority_min] sys_sched_get_priority_min, + [__NR_sched_rr_get_interval] sys_sched_rr_get_interval, + [__NR_nanosleep] sys_nanosleep, + [__NR_mremap] sys_mremap, + [__NR_setresuid] sys_setresuid, + [__NR_getresuid] sys_getresuid, + [__NR_query_module] sys_ni_syscall, + [__NR_poll] sys_poll, + [__NR_nfsservctl] sys_ni_syscall, /* sys_nfsservctl */ + [__NR_setresgid] sys_setresgid, + [__NR_getresgid] sys_getresgid, + [__NR_prctl] sys_prctl, + [__NR_rt_sigreturn] sys_ni_syscall, /* ppc64_rt_sigreturn */ + [__NR_rt_sigaction] sys_ni_syscall, /* sys_rt_sigaction */ + [__NR_rt_sigprocmask] sys_ni_syscall, /* sys_rt_sigprocmask */ + [__NR_rt_sigpending] sys_ni_syscall, /* sys_rt_sigpending */ + [__NR_rt_sigtimedwait] sys_ni_syscall, /* sys_rt_sigtimedwait */ + [__NR_rt_sigqueueinfo] sys_ni_syscall, /* sys_rt_sigqueueinfo */ + [__NR_rt_sigsuspend] sys_ni_syscall, /* sys_rt_sigsuspend */ + [__NR_pread64] sys_pread64, + [__NR_pwrite64] sys_pwrite64, + [__NR_chown] sys_chown, + [__NR_getcwd] sys_getcwd, + [__NR_capget] sys_capget, + [__NR_capset] sys_capset, + [__NR_sigaltstack] sys_ni_syscall, /* sys_sigaltstack */ + [__NR_sendfile] sys_sendfile64, + [__NR_getpmsg] sys_ni_syscall, + [__NR_putpmsg] sys_ni_syscall, + [__NR_vfork] sys_ni_syscall, /* ppc_vfork */ + [__NR_ugetrlimit] sys_getrlimit, + [__NR_readahead] sys_readahead, + [192] sys_ni_syscall, + [193] sys_ni_syscall, + [194] sys_ni_syscall, + [195] sys_ni_syscall, + [196] sys_ni_syscall, + [197] sys_ni_syscall, + [__NR_pciconfig_read] sys_ni_syscall, /* sys_pciconfig_read */ + [__NR_pciconfig_write] sys_ni_syscall, /* sys_pciconfig_write */ + [__NR_pciconfig_iobase] sys_ni_syscall, /* sys_pciconfig_iobase */ + [__NR_multiplexer] sys_ni_syscall, + [__NR_getdents64] sys_getdents64, + [__NR_pivot_root] sys_pivot_root, + [204] sys_ni_syscall, + [__NR_madvise] sys_madvise, + [__NR_mincore] sys_mincore, + [__NR_gettid] sys_gettid, + [__NR_tkill] sys_tkill, + [__NR_setxattr] sys_setxattr, + [__NR_lsetxattr] sys_lsetxattr, + [__NR_fsetxattr] sys_fsetxattr, + [__NR_getxattr] sys_getxattr, + [__NR_lgetxattr] sys_lgetxattr, + [__NR_fgetxattr] sys_fgetxattr, + [__NR_listxattr] sys_listxattr, + [__NR_llistxattr] sys_llistxattr, + [__NR_flistxattr] sys_flistxattr, + [__NR_removexattr] sys_removexattr, + [__NR_lremovexattr] sys_lremovexattr, + [__NR_fremovexattr] sys_fremovexattr, + [__NR_futex] sys_futex, + [__NR_sched_setaffinity] sys_sched_setaffinity, + [__NR_sched_getaffinity] sys_sched_getaffinity, + [__NR_tuxcall] sys_ni_syscall, + [226] sys_ni_syscall, + [__NR_io_setup] sys_io_setup, + [__NR_io_destroy] sys_io_destroy, + [__NR_io_getevents] sys_io_getevents, + [__NR_io_submit] sys_io_submit, + [__NR_io_cancel] sys_io_cancel, + [__NR_set_tid_address] sys_ni_syscall, /* sys_set_tid_address */ + [__NR_fadvise64] sys_fadvise64, + [__NR_exit_group] sys_ni_syscall, /* sys_exit_group */ + [__NR_lookup_dcookie] sys_ni_syscall, /* sys_lookup_dcookie */ + [__NR_epoll_create] sys_epoll_create, + [__NR_epoll_ctl] sys_epoll_ctl, + [__NR_epoll_wait] sys_epoll_wait, + [__NR_remap_file_pages] sys_remap_file_pages, + [__NR_timer_create] sys_timer_create, + [__NR_timer_settime] sys_timer_settime, + [__NR_timer_gettime] sys_timer_gettime, + [__NR_timer_getoverrun] sys_timer_getoverrun, + [__NR_timer_delete] sys_timer_delete, + [__NR_clock_settime] sys_clock_settime, + [__NR_clock_gettime] sys_clock_gettime, + [__NR_clock_getres] sys_clock_getres, + [__NR_clock_nanosleep] sys_clock_nanosleep, + [__NR_swapcontext] sys_ni_syscall, /* ppc64_swapcontext */ + [__NR_tgkill] sys_tgkill, + [__NR_utimes] sys_utimes, + [__NR_statfs64] sys_statfs64, + [__NR_fstatfs64] sys_fstatfs64, + [254] sys_ni_syscall, + [__NR_rtas] ppc_rtas, + [256] sys_ni_syscall, + [257] sys_ni_syscall, + [258] sys_ni_syscall, + [__NR_mbind] sys_ni_syscall, /* sys_mbind */ + [__NR_get_mempolicy] sys_ni_syscall, /* sys_get_mempolicy */ + [__NR_set_mempolicy] sys_ni_syscall, /* sys_set_mempolicy */ + [__NR_mq_open] sys_ni_syscall, /* sys_mq_open */ + [__NR_mq_unlink] sys_ni_syscall, /* sys_mq_unlink */ + [__NR_mq_timedsend] sys_ni_syscall, /* sys_mq_timedsend */ + [__NR_mq_timedreceive] sys_ni_syscall, /* sys_mq_timedreceive */ + [__NR_mq_notify] sys_ni_syscall, /* sys_mq_notify */ + [__NR_mq_getsetattr] sys_ni_syscall, /* sys_mq_getsetattr */ + [__NR_kexec_load] sys_ni_syscall, /* sys_kexec_load */ + [__NR_add_key] sys_ni_syscall, /* sys_add_key */ + [__NR_request_key] sys_ni_syscall, /* sys_request_key */ + [__NR_keyctl] sys_ni_syscall, /* sys_keyctl */ + [__NR_waitid] sys_ni_syscall, /* sys_waitid */ + [__NR_ioprio_set] sys_ni_syscall, /* sys_ioprio_set */ + [__NR_ioprio_get] sys_ni_syscall, /* sys_ioprio_get */ + [__NR_inotify_init] sys_ni_syscall, /* sys_inotify_init */ + [__NR_inotify_add_watch] sys_ni_syscall, /* sys_inotify_add_watch */ + [__NR_inotify_rm_watch] sys_ni_syscall, /* sys_inotify_rm_watch */ + [__NR_spu_run] sys_ni_syscall, /* sys_spu_run */ + [__NR_spu_create] sys_ni_syscall, /* sys_spu_create */ + [__NR_pselect6] sys_ni_syscall, /* sys_pselect */ + [__NR_ppoll] sys_ni_syscall, /* sys_ppoll */ + [__NR_unshare] sys_unshare, +}; + +long spu_sys_callback(struct spu_syscall_block *s) +{ + long (*syscall)(u64 a1, u64 a2, u64 a3, u64 a4, u64 a5, u64 a6); + + BUILD_BUG_ON(ARRAY_SIZE(spu_syscall_table) != __NR_syscalls); + + syscall = spu_syscall_table[s->nr_ret]; + + if (s->nr_ret >= __NR_syscalls) { + pr_debug("%s: invalid syscall #%ld", __FUNCTION__, s->nr_ret); + return -ENOSYS; + } + +#ifdef DEBUG + print_symbol(KERN_DEBUG "SPU-syscall %s:", (unsigned long)syscall); + printk("syscall%ld(%lx, %lx, %lx, %lx, %lx, %lx)\n", + s->nr_ret, + s->parm[0], s->parm[1], s->parm[2], + s->parm[3], s->parm[4], s->parm[5]); +#endif + + return syscall(s->parm[0], s->parm[1], s->parm[2], + s->parm[3], s->parm[4], s->parm[5]); +} +EXPORT_SYMBOL_GPL(spu_sys_callback); diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 18ea8866c61a..c04e078c0fe5 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -76,6 +76,90 @@ static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc, return 0; } +/* + * SPU syscall restarting is tricky because we violate the basic + * assumption that the signal handler is running on the interrupted + * thread. Here instead, the handler runs on PowerPC user space code, + * while the syscall was called from the SPU. + * This means we can only do a very rough approximation of POSIX + * signal semantics. + */ +int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret, + unsigned int *npc) +{ + int ret; + + switch (*spu_ret) { + case -ERESTARTSYS: + case -ERESTARTNOINTR: + /* + * Enter the regular syscall restarting for + * sys_spu_run, then restart the SPU syscall + * callback. + */ + *npc -= 8; + ret = -ERESTARTSYS; + break; + case -ERESTARTNOHAND: + case -ERESTART_RESTARTBLOCK: + /* + * Restart block is too hard for now, just return -EINTR + * to the SPU. + * ERESTARTNOHAND comes from sys_pause, we also return + * -EINTR from there. + * Assume that we need to be restarted ourselves though. + */ + *spu_ret = -EINTR; + ret = -ERESTARTSYS; + break; + default: + printk(KERN_WARNING "%s: unexpected return code %ld\n", + __FUNCTION__, *spu_ret); + ret = 0; + } + return ret; +} + +int spu_process_callback(struct spu_context *ctx) +{ + struct spu_syscall_block s; + u32 ls_pointer, npc; + char *ls; + long spu_ret; + int ret; + + /* get syscall block from local store */ + npc = ctx->ops->npc_read(ctx); + ls = ctx->ops->get_ls(ctx); + ls_pointer = *(u32*)(ls + npc); + if (ls_pointer > (LS_SIZE - sizeof(s))) + return -EFAULT; + memcpy(&s, ls + ls_pointer, sizeof (s)); + + /* do actual syscall without pinning the spu */ + ret = 0; + spu_ret = -ENOSYS; + npc += 4; + + if (s.nr_ret < __NR_syscalls) { + spu_release(ctx); + /* do actual system call from here */ + spu_ret = spu_sys_callback(&s); + if (spu_ret <= -ERESTARTSYS) { + ret = spu_handle_restartsys(ctx, &spu_ret, &npc); + } + spu_acquire(ctx); + if (ret == -ERESTARTSYS) + return ret; + } + + /* write result, jump over indirect pointer */ + memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret)); + ctx->ops->npc_write(ctx, npc); + ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); + return ret; +} + static inline int spu_process_events(struct spu_context *ctx) { struct spu *spu = ctx->spu; @@ -107,6 +191,13 @@ long spufs_run_spu(struct file *file, struct spu_context *ctx, ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, status)); if (unlikely(ret)) break; + if ((*status & SPU_STATUS_STOPPED_BY_STOP) && + (*status >> SPU_STOP_STATUS_SHIFT == 0x2104)) { + ret = spu_process_callback(ctx); + if (ret) + break; + *status &= ~SPU_STATUS_STOPPED_BY_STOP; + } if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) { ret = spu_reacquire_runnable(ctx, npc, status); if (ret) diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h index 38bacf2f6e0c..b5c90d6fdceb 100644 --- a/include/asm-powerpc/spu.h +++ b/include/asm-powerpc/spu.h @@ -149,6 +149,14 @@ int spu_irq_class_0_bottom(struct spu *spu); int spu_irq_class_1_bottom(struct spu *spu); void spu_irq_setaffinity(struct spu *spu, int cpu); +/* system callbacks from the SPU */ +struct spu_syscall_block { + u64 nr_ret; + u64 parm[6]; +}; +extern long spu_sys_callback(struct spu_syscall_block *s); + +/* syscalls implemented in spufs */ extern struct spufs_calls { asmlinkage long (*create_thread)(const char __user *name, unsigned int flags, mode_t mode); @@ -399,7 +407,6 @@ struct spu_priv1 { #define SPU_GET_REVISION_BITS(vr) (vr & SPU_REVISION_BITS) u8 pad_0x28_0x100[0x100 - 0x28]; /* 0x28 */ - /* Interrupt Area */ u64 int_mask_RW[3]; /* 0x100 */ #define CLASS0_ENABLE_DMA_ALIGNMENT_INTR 0x1L -- cgit v1.2.3 From a33a7d7309d79656bc19a0e96fc4547a1633283e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 23 Mar 2006 00:00:11 +0100 Subject: [PATCH] spufs: implement mfc access for PPE-side DMA This patch adds a new file called 'mfc' to each spufs directory. The file accepts DMA commands that are a subset of what would be legal DMA commands for problem state register access. Upon reading the file, a bitmask is returned with the completed tag groups set. The file is meant to be used from an abstraction in libspe that is added by a different patch. From the kernel perspective, this means a process can now offload a memory copy from or into an SPE local store without having to run code on the SPE itself. The transfer will only be performed while the SPE is owned by one thread that is waiting in the spu_run system call and the data will be transferred into that thread's address space, independent of which thread started the transfer. Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/cell/spu_base.c | 7 +- arch/powerpc/platforms/cell/spufs/backing_ops.c | 47 ++++ arch/powerpc/platforms/cell/spufs/context.c | 5 +- arch/powerpc/platforms/cell/spufs/file.c | 294 ++++++++++++++++++++++++ arch/powerpc/platforms/cell/spufs/hw_ops.c | 57 +++++ arch/powerpc/platforms/cell/spufs/sched.c | 2 + arch/powerpc/platforms/cell/spufs/spufs.h | 20 ++ arch/powerpc/platforms/cell/spufs/switch.c | 3 +- include/asm-powerpc/spu.h | 1 + 9 files changed, 431 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index a8fa1eeeb174..162b6cfa8a43 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -111,7 +111,7 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea) extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap); //XXX static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr) { - pr_debug("%s\n", __FUNCTION__); + pr_debug("%s, %lx, %lx\n", __FUNCTION__, dsisr, ea); /* Handle kernel space hash faults immediately. User hash faults need to be deferred to process context. */ @@ -168,7 +168,7 @@ static int __spu_trap_halt(struct spu *spu) static int __spu_trap_tag_group(struct spu *spu) { pr_debug("%s\n", __FUNCTION__); - /* wake_up(&spu->dma_wq); */ + spu->mfc_callback(spu); return 0; } @@ -242,6 +242,8 @@ spu_irq_class_1(int irq, void *data, struct pt_regs *regs) spu_mfc_dsisr_set(spu, 0ul); spu_int_stat_clear(spu, 1, stat); spin_unlock(&spu->register_lock); + pr_debug("%s: %lx %lx %lx %lx\n", __FUNCTION__, mask, stat, + dar, dsisr); if (stat & 1) /* segment fault */ __spu_trap_data_seg(spu, dar); @@ -632,6 +634,7 @@ static int __init create_spu(struct device_node *spe) spu->ibox_callback = NULL; spu->wbox_callback = NULL; spu->stop_callback = NULL; + spu->mfc_callback = NULL; mutex_lock(&spu_mutex); spu->number = number++; diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c index a5c489a53c61..f1d35ddc9df3 100644 --- a/arch/powerpc/platforms/cell/spufs/backing_ops.c +++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -285,6 +285,49 @@ static void spu_backing_runcntl_stop(struct spu_context *ctx) spu_backing_runcntl_write(ctx, SPU_RUNCNTL_STOP); } +static int spu_backing_set_mfc_query(struct spu_context * ctx, u32 mask, + u32 mode) +{ + struct spu_problem_collapsed *prob = &ctx->csa.prob; + int ret; + + spin_lock(&ctx->csa.register_lock); + ret = -EAGAIN; + if (prob->dma_querytype_RW) + goto out; + ret = 0; + /* FIXME: what are the side-effects of this? */ + prob->dma_querymask_RW = mask; + prob->dma_querytype_RW = mode; +out: + spin_unlock(&ctx->csa.register_lock); + + return ret; +} + +static u32 spu_backing_read_mfc_tagstatus(struct spu_context * ctx) +{ + return ctx->csa.prob.dma_tagstatus_R; +} + +static u32 spu_backing_get_mfc_free_elements(struct spu_context *ctx) +{ + return ctx->csa.prob.dma_qstatus_R; +} + +static int spu_backing_send_mfc_command(struct spu_context *ctx, + struct mfc_dma_command *cmd) +{ + int ret; + + spin_lock(&ctx->csa.register_lock); + ret = -EAGAIN; + /* FIXME: set up priv2->puq */ + spin_unlock(&ctx->csa.register_lock); + + return ret; +} + struct spu_context_ops spu_backing_ops = { .mbox_read = spu_backing_mbox_read, .mbox_stat_read = spu_backing_mbox_stat_read, @@ -305,4 +348,8 @@ struct spu_context_ops spu_backing_ops = { .get_ls = spu_backing_get_ls, .runcntl_write = spu_backing_runcntl_write, .runcntl_stop = spu_backing_runcntl_stop, + .set_mfc_query = spu_backing_set_mfc_query, + .read_mfc_tagstatus = spu_backing_read_mfc_tagstatus, + .get_mfc_free_elements = spu_backing_get_mfc_free_elements, + .send_mfc_command = spu_backing_send_mfc_command, }; diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 336f238102fd..7e016b9eab21 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -47,8 +47,11 @@ struct spu_context *alloc_spu_context(struct address_space *local_store) init_waitqueue_head(&ctx->ibox_wq); init_waitqueue_head(&ctx->wbox_wq); init_waitqueue_head(&ctx->stop_wq); + init_waitqueue_head(&ctx->mfc_wq); ctx->ibox_fasync = NULL; ctx->wbox_fasync = NULL; + ctx->mfc_fasync = NULL; + ctx->tagwait = 0; ctx->state = SPU_STATE_SAVED; ctx->local_store = local_store; ctx->spu = NULL; @@ -68,8 +71,6 @@ void destroy_spu_context(struct kref *kref) ctx = container_of(kref, struct spu_context, kref); down_write(&ctx->state_sema); spu_deactivate(ctx); - ctx->ibox_fasync = NULL; - ctx->wbox_fasync = NULL; up_write(&ctx->state_sema); spu_fini_csa(&ctx->csa); kfree(ctx); diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index dfa649c9b956..62fe9941ccee 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -20,6 +20,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#undef DEBUG + #include #include #include @@ -641,6 +643,297 @@ static u64 spufs_signal2_type_get(void *data) DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, spufs_signal2_type_set, "%llu"); + +static int spufs_mfc_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + /* we don't want to deal with DMA into other processes */ + if (ctx->owner != current->mm) + return -EINVAL; + + if (atomic_read(&inode->i_count) != 1) + return -EBUSY; + + file->private_data = ctx; + return nonseekable_open(inode, file); +} + +/* interrupt-level mfc callback function. */ +void spufs_mfc_callback(struct spu *spu) +{ + struct spu_context *ctx = spu->ctx; + + wake_up_all(&ctx->mfc_wq); + + pr_debug("%s %s\n", __FUNCTION__, spu->name); + if (ctx->mfc_fasync) { + u32 free_elements, tagstatus; + unsigned int mask; + + /* no need for spu_acquire in interrupt context */ + free_elements = ctx->ops->get_mfc_free_elements(ctx); + tagstatus = ctx->ops->read_mfc_tagstatus(ctx); + + mask = 0; + if (free_elements & 0xffff) + mask |= POLLOUT; + if (tagstatus & ctx->tagwait) + mask |= POLLIN; + + kill_fasync(&ctx->mfc_fasync, SIGIO, mask); + } +} + +static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) +{ + /* See if there is one tag group is complete */ + /* FIXME we need locking around tagwait */ + *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; + ctx->tagwait &= ~*status; + if (*status) + return 1; + + /* enable interrupt waiting for any tag group, + may silently fail if interrupts are already enabled */ + ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); + return 0; +} + +static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + int ret = -EINVAL; + u32 status; + + if (size != 4) + goto out; + + spu_acquire(ctx); + if (file->f_flags & O_NONBLOCK) { + status = ctx->ops->read_mfc_tagstatus(ctx); + if (!(status & ctx->tagwait)) + ret = -EAGAIN; + else + ctx->tagwait &= ~status; + } else { + ret = spufs_wait(ctx->mfc_wq, + spufs_read_mfc_tagstatus(ctx, &status)); + } + spu_release(ctx); + + if (ret) + goto out; + + ret = 4; + if (copy_to_user(buffer, &status, 4)) + ret = -EFAULT; + +out: + return ret; +} + +static int spufs_check_valid_dma(struct mfc_dma_command *cmd) +{ + pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa, + cmd->ea, cmd->size, cmd->tag, cmd->cmd); + + switch (cmd->cmd) { + case MFC_PUT_CMD: + case MFC_PUTF_CMD: + case MFC_PUTB_CMD: + case MFC_GET_CMD: + case MFC_GETF_CMD: + case MFC_GETB_CMD: + break; + default: + pr_debug("invalid DMA opcode %x\n", cmd->cmd); + return -EIO; + } + + if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { + pr_debug("invalid DMA alignment, ea %lx lsa %x\n", + cmd->ea, cmd->lsa); + return -EIO; + } + + switch (cmd->size & 0xf) { + case 1: + break; + case 2: + if (cmd->lsa & 1) + goto error; + break; + case 4: + if (cmd->lsa & 3) + goto error; + break; + case 8: + if (cmd->lsa & 7) + goto error; + break; + case 0: + if (cmd->lsa & 15) + goto error; + break; + error: + default: + pr_debug("invalid DMA alignment %x for size %x\n", + cmd->lsa & 0xf, cmd->size); + return -EIO; + } + + if (cmd->size > 16 * 1024) { + pr_debug("invalid DMA size %x\n", cmd->size); + return -EIO; + } + + if (cmd->tag & 0xfff0) { + /* we reserve the higher tag numbers for kernel use */ + pr_debug("invalid DMA tag\n"); + return -EIO; + } + + if (cmd->class) { + /* not supported in this version */ + pr_debug("invalid DMA class\n"); + return -EIO; + } + + return 0; +} + +static int spu_send_mfc_command(struct spu_context *ctx, + struct mfc_dma_command cmd, + int *error) +{ + *error = ctx->ops->send_mfc_command(ctx, &cmd); + if (*error == -EAGAIN) { + /* wait for any tag group to complete + so we have space for the new command */ + ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); + /* try again, because the queue might be + empty again */ + *error = ctx->ops->send_mfc_command(ctx, &cmd); + if (*error == -EAGAIN) + return 0; + } + return 1; +} + +static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + struct mfc_dma_command cmd; + int ret = -EINVAL; + + if (size != sizeof cmd) + goto out; + + ret = -EFAULT; + if (copy_from_user(&cmd, buffer, sizeof cmd)) + goto out; + + ret = spufs_check_valid_dma(&cmd); + if (ret) + goto out; + + spu_acquire_runnable(ctx); + if (file->f_flags & O_NONBLOCK) { + ret = ctx->ops->send_mfc_command(ctx, &cmd); + } else { + int status; + ret = spufs_wait(ctx->mfc_wq, + spu_send_mfc_command(ctx, cmd, &status)); + if (status) + ret = status; + } + spu_release(ctx); + + if (ret) + goto out; + + ctx->tagwait |= 1 << cmd.tag; + +out: + return ret; +} + +static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) +{ + struct spu_context *ctx = file->private_data; + u32 free_elements, tagstatus; + unsigned int mask; + + spu_acquire(ctx); + ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); + free_elements = ctx->ops->get_mfc_free_elements(ctx); + tagstatus = ctx->ops->read_mfc_tagstatus(ctx); + spu_release(ctx); + + poll_wait(file, &ctx->mfc_wq, wait); + + mask = 0; + if (free_elements & 0xffff) + mask |= POLLOUT | POLLWRNORM; + if (tagstatus & ctx->tagwait) + mask |= POLLIN | POLLRDNORM; + + pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__, + free_elements, tagstatus, ctx->tagwait); + + return mask; +} + +static int spufs_mfc_flush(struct file *file) +{ + struct spu_context *ctx = file->private_data; + int ret; + + spu_acquire(ctx); +#if 0 +/* this currently hangs */ + ret = spufs_wait(ctx->mfc_wq, + ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); + if (ret) + goto out; + ret = spufs_wait(ctx->mfc_wq, + ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); +out: +#else + ret = 0; +#endif + spu_release(ctx); + + return ret; +} + +static int spufs_mfc_fsync(struct file *file, struct dentry *dentry, + int datasync) +{ + return spufs_mfc_flush(file); +} + +static int spufs_mfc_fasync(int fd, struct file *file, int on) +{ + struct spu_context *ctx = file->private_data; + + return fasync_helper(fd, file, on, &ctx->mfc_fasync); +} + +static struct file_operations spufs_mfc_fops = { + .open = spufs_mfc_open, + .read = spufs_mfc_read, + .write = spufs_mfc_write, + .poll = spufs_mfc_poll, + .flush = spufs_mfc_flush, + .fsync = spufs_mfc_fsync, + .fasync = spufs_mfc_fasync, +}; + static void spufs_npc_set(void *data, u64 val) { struct spu_context *ctx = data; @@ -783,6 +1076,7 @@ struct tree_descr spufs_dir_contents[] = { { "signal2", &spufs_signal2_fops, 0666, }, { "signal1_type", &spufs_signal1_type, 0666, }, { "signal2_type", &spufs_signal2_type, 0666, }, + { "mfc", &spufs_mfc_fops, 0666, }, { "npc", &spufs_npc_ops, 0666, }, { "fpcr", &spufs_fpcr_fops, 0666, }, { "decr", &spufs_decr_ops, 0666, }, diff --git a/arch/powerpc/platforms/cell/spufs/hw_ops.c b/arch/powerpc/platforms/cell/spufs/hw_ops.c index 5445719bff79..a13a8b5a014d 100644 --- a/arch/powerpc/platforms/cell/spufs/hw_ops.c +++ b/arch/powerpc/platforms/cell/spufs/hw_ops.c @@ -232,6 +232,59 @@ static void spu_hw_runcntl_stop(struct spu_context *ctx) spin_unlock_irq(&ctx->spu->register_lock); } +static int spu_hw_set_mfc_query(struct spu_context * ctx, u32 mask, u32 mode) +{ + struct spu_problem *prob = ctx->spu->problem; + int ret; + + spin_lock_irq(&ctx->spu->register_lock); + ret = -EAGAIN; + if (in_be32(&prob->dma_querytype_RW)) + goto out; + ret = 0; + out_be32(&prob->dma_querymask_RW, mask); + out_be32(&prob->dma_querytype_RW, mode); +out: + spin_unlock_irq(&ctx->spu->register_lock); + return ret; +} + +static u32 spu_hw_read_mfc_tagstatus(struct spu_context * ctx) +{ + return in_be32(&ctx->spu->problem->dma_tagstatus_R); +} + +static u32 spu_hw_get_mfc_free_elements(struct spu_context *ctx) +{ + return in_be32(&ctx->spu->problem->dma_qstatus_R); +} + +static int spu_hw_send_mfc_command(struct spu_context *ctx, + struct mfc_dma_command *cmd) +{ + u32 status; + struct spu_problem *prob = ctx->spu->problem; + + spin_lock_irq(&ctx->spu->register_lock); + out_be32(&prob->mfc_lsa_W, cmd->lsa); + out_be64(&prob->mfc_ea_W, cmd->ea); + out_be32(&prob->mfc_union_W.by32.mfc_size_tag32, + cmd->size << 16 | cmd->tag); + out_be32(&prob->mfc_union_W.by32.mfc_class_cmd32, + cmd->class << 16 | cmd->cmd); + status = in_be32(&prob->mfc_union_W.by32.mfc_class_cmd32); + spin_unlock_irq(&ctx->spu->register_lock); + + switch (status & 0xffff) { + case 0: + return 0; + case 2: + return -EAGAIN; + default: + return -EINVAL; + } +} + struct spu_context_ops spu_hw_ops = { .mbox_read = spu_hw_mbox_read, .mbox_stat_read = spu_hw_mbox_stat_read, @@ -252,4 +305,8 @@ struct spu_context_ops spu_hw_ops = { .get_ls = spu_hw_get_ls, .runcntl_write = spu_hw_runcntl_write, .runcntl_stop = spu_hw_runcntl_stop, + .set_mfc_query = spu_hw_set_mfc_query, + .read_mfc_tagstatus = spu_hw_read_mfc_tagstatus, + .get_mfc_free_elements = spu_hw_get_mfc_free_elements, + .send_mfc_command = spu_hw_send_mfc_command, }; diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 963182fbd1aa..bf652cd77000 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -180,6 +180,7 @@ static inline void bind_context(struct spu *spu, struct spu_context *ctx) spu->ibox_callback = spufs_ibox_callback; spu->wbox_callback = spufs_wbox_callback; spu->stop_callback = spufs_stop_callback; + spu->mfc_callback = spufs_mfc_callback; mb(); spu_unmap_mappings(ctx); spu_restore(&ctx->csa, spu); @@ -197,6 +198,7 @@ static inline void unbind_context(struct spu *spu, struct spu_context *ctx) spu->ibox_callback = NULL; spu->wbox_callback = NULL; spu->stop_callback = NULL; + spu->mfc_callback = NULL; spu->mm = NULL; spu->pid = 0; spu->prio = MAX_PRIO; diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index db2601f0abd5..57d687ca3f03 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -55,13 +55,27 @@ struct spu_context { wait_queue_head_t ibox_wq; wait_queue_head_t wbox_wq; wait_queue_head_t stop_wq; + wait_queue_head_t mfc_wq; struct fasync_struct *ibox_fasync; struct fasync_struct *wbox_fasync; + struct fasync_struct *mfc_fasync; + u32 tagwait; struct spu_context_ops *ops; struct work_struct reap_work; u64 flags; }; +struct mfc_dma_command { + int32_t pad; /* reserved */ + uint32_t lsa; /* local storage address */ + uint64_t ea; /* effective address */ + uint16_t size; /* transfer size */ + uint16_t tag; /* command tag */ + uint16_t class; /* class ID */ + uint16_t cmd; /* command opcode */ +}; + + /* SPU context query/set operations. */ struct spu_context_ops { int (*mbox_read) (struct spu_context * ctx, u32 * data); @@ -84,6 +98,11 @@ struct spu_context_ops { char*(*get_ls) (struct spu_context * ctx); void (*runcntl_write) (struct spu_context * ctx, u32 data); void (*runcntl_stop) (struct spu_context * ctx); + int (*set_mfc_query)(struct spu_context * ctx, u32 mask, u32 mode); + u32 (*read_mfc_tagstatus)(struct spu_context * ctx); + u32 (*get_mfc_free_elements)(struct spu_context *ctx); + int (*send_mfc_command)(struct spu_context *ctx, + struct mfc_dma_command *cmd); }; extern struct spu_context_ops spu_hw_ops; @@ -159,5 +178,6 @@ size_t spu_ibox_read(struct spu_context *ctx, u32 *data); void spufs_ibox_callback(struct spu *spu); void spufs_wbox_callback(struct spu *spu); void spufs_stop_callback(struct spu *spu); +void spufs_mfc_callback(struct spu *spu); #endif diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c index 212db28531fa..97898d5d34e5 100644 --- a/arch/powerpc/platforms/cell/spufs/switch.c +++ b/arch/powerpc/platforms/cell/spufs/switch.c @@ -2145,7 +2145,8 @@ static void init_priv1(struct spu_state *csa) csa->priv1.int_mask_class1_RW = CLASS1_ENABLE_SEGMENT_FAULT_INTR | CLASS1_ENABLE_STORAGE_FAULT_INTR; csa->priv1.int_mask_class2_RW = CLASS2_ENABLE_SPU_STOP_INTR | - CLASS2_ENABLE_SPU_HALT_INTR; + CLASS2_ENABLE_SPU_HALT_INTR | + CLASS2_ENABLE_SPU_DMA_TAG_GROUP_COMPLETE_INTR; } static void init_priv2(struct spu_state *csa) diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h index b5c90d6fdceb..8564b8234069 100644 --- a/include/asm-powerpc/spu.h +++ b/include/asm-powerpc/spu.h @@ -137,6 +137,7 @@ struct spu { void (* wbox_callback)(struct spu *spu); void (* ibox_callback)(struct spu *spu); void (* stop_callback)(struct spu *spu); + void (* mfc_callback)(struct spu *spu); char irq_c0[8]; char irq_c1[8]; -- cgit v1.2.3 From 6df10a82f8de89c66eb91c371d62d76e87b2cbba Mon Sep 17 00:00:00 2001 From: Mark Nutter Date: Thu, 23 Mar 2006 00:00:12 +0100 Subject: [PATCH] spufs: enable SPE problem state MMIO access. This patch is layered on top of CONFIG_SPARSEMEM and is patterned after direct mapping of LS. This patch allows mmap() of the following regions: "mfc", which represents the area from [0x3000 - 0x3fff]; "cntl", which represents the area from [0x4000 - 0x4fff]; "signal1" which begins at offset 0x14000; "signal2" which begins at offset 0x1c000. The signal1 & signal2 files may be mmap()'d by regular user processes. The cntl and mfc file, on the other hand, may only be accessed if the owning process has CAP_SYS_RAWIO, because they have the potential to confuse the kernel with regard to parallel access to the same files with regular file operations: the kernel always holds a spinlock when accessing registers in these areas to serialize them, which can not be guaranteed with user mmaps, Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/cell/Kconfig | 5 + arch/powerpc/platforms/cell/spu_base.c | 5 + arch/powerpc/platforms/cell/spufs/context.c | 18 ++- arch/powerpc/platforms/cell/spufs/file.c | 229 +++++++++++++++++++++++++++- arch/powerpc/platforms/cell/spufs/inode.c | 2 +- arch/powerpc/platforms/cell/spufs/spufs.h | 8 +- include/asm-powerpc/spu.h | 1 + 7 files changed, 256 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 3157071e241c..c2a3db8edb0c 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig @@ -10,4 +10,9 @@ config SPU_FS Units on machines implementing the Broadband Processor Architecture. +config SPUFS_MMAP + bool + depends on SPU_FS && SPARSEMEM && !PPC_64K_PAGES + default y + endmenu diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 162b6cfa8a43..d152a3fbdb83 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -570,6 +570,11 @@ static int __init spu_map_device(struct spu *spu, struct device_node *spe) if (!spu->local_store) goto out; + prop = get_property(spe, "problem", NULL); + if (!prop) + goto out_unmap; + spu->problem_phys = *(unsigned long *)prop; + spu->problem= map_spe_prop(spe, "problem"); if (!spu->problem) goto out_unmap; diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 7e016b9eab21..3f75c1e7adea 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -27,7 +27,7 @@ #include #include "spufs.h" -struct spu_context *alloc_spu_context(struct address_space *local_store) +struct spu_context *alloc_spu_context(void) { struct spu_context *ctx; ctx = kmalloc(sizeof *ctx, GFP_KERNEL); @@ -53,7 +53,10 @@ struct spu_context *alloc_spu_context(struct address_space *local_store) ctx->mfc_fasync = NULL; ctx->tagwait = 0; ctx->state = SPU_STATE_SAVED; - ctx->local_store = local_store; + ctx->local_store = NULL; + ctx->cntl = NULL; + ctx->signal1 = NULL; + ctx->signal2 = NULL; ctx->spu = NULL; ctx->ops = &spu_backing_ops; ctx->owner = get_task_mm(current); @@ -110,7 +113,16 @@ void spu_release(struct spu_context *ctx) void spu_unmap_mappings(struct spu_context *ctx) { - unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1); + if (ctx->local_store) + unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1); + if (ctx->mfc) + unmap_mapping_range(ctx->mfc, 0, 0x4000, 1); + if (ctx->cntl) + unmap_mapping_range(ctx->cntl, 0, 0x4000, 1); + if (ctx->signal1) + unmap_mapping_range(ctx->signal1, 0, 0x4000, 1); + if (ctx->signal2) + unmap_mapping_range(ctx->signal2, 0, 0x4000, 1); } int spu_acquire_runnable(struct spu_context *ctx) diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 62fe9941ccee..366185e92667 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -41,8 +41,10 @@ static int spufs_mem_open(struct inode *inode, struct file *file) { struct spufs_inode_info *i = SPUFS_I(inode); - file->private_data = i->i_ctx; - file->f_mapping = i->i_ctx->local_store; + struct spu_context *ctx = i->i_ctx; + file->private_data = ctx; + file->f_mapping = inode->i_mapping; + ctx->local_store = inode->i_mapping; return 0; } @@ -86,7 +88,7 @@ spufs_mem_write(struct file *file, const char __user *buffer, return ret; } -#ifdef CONFIG_SPARSEMEM +#ifdef CONFIG_SPUFS_MMAP static struct page * spufs_mem_mmap_nopage(struct vm_area_struct *vma, unsigned long address, int *type) @@ -138,11 +140,113 @@ static struct file_operations spufs_mem_fops = { .read = spufs_mem_read, .write = spufs_mem_write, .llseek = generic_file_llseek, -#ifdef CONFIG_SPARSEMEM +#ifdef CONFIG_SPUFS_MMAP .mmap = spufs_mem_mmap, #endif }; +#ifdef CONFIG_SPUFS_MMAP +static struct page *spufs_ps_nopage(struct vm_area_struct *vma, + unsigned long address, + int *type, unsigned long ps_offs) +{ + struct page *page = NOPAGE_SIGBUS; + int fault_type = VM_FAULT_SIGBUS; + struct spu_context *ctx = vma->vm_file->private_data; + unsigned long offset = address - vma->vm_start; + unsigned long area; + int ret; + + offset += vma->vm_pgoff << PAGE_SHIFT; + if (offset >= 0x4000) + goto out; + + ret = spu_acquire_runnable(ctx); + if (ret) + goto out; + + area = ctx->spu->problem_phys + ps_offs; + page = pfn_to_page((area + offset) >> PAGE_SHIFT); + fault_type = VM_FAULT_MINOR; + page_cache_get(page); + + spu_release(ctx); + + out: + if (type) + *type = fault_type; + + return page; +} + +static struct page *spufs_cntl_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return spufs_ps_nopage(vma, address, type, 0x4000); +} + +static struct vm_operations_struct spufs_cntl_mmap_vmops = { + .nopage = spufs_cntl_mmap_nopage, +}; + +/* + * mmap support for problem state control area [0x4000 - 0x4fff]. + * Mapping this area requires that the application have CAP_SYS_RAWIO, + * as these registers require special care when read/writing. + */ +static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) + | _PAGE_NO_CACHE); + + vma->vm_ops = &spufs_cntl_mmap_vmops; + return 0; +} +#endif + +static int spufs_cntl_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + file->private_data = ctx; + file->f_mapping = inode->i_mapping; + ctx->cntl = inode->i_mapping; + return 0; +} + +static ssize_t +spufs_cntl_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + /* FIXME: read from spu status */ + return -EINVAL; +} + +static ssize_t +spufs_cntl_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + /* FIXME: write to runctl bit */ + return -EINVAL; +} + +static struct file_operations spufs_cntl_fops = { + .open = spufs_cntl_open, + .read = spufs_cntl_read, + .write = spufs_cntl_write, +#ifdef CONFIG_SPUFS_MMAP + .mmap = spufs_cntl_mmap, +#endif +}; + static int spufs_regs_open(struct inode *inode, struct file *file) { @@ -503,6 +607,16 @@ static struct file_operations spufs_wbox_stat_fops = { .read = spufs_wbox_stat_read, }; +static int spufs_signal1_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + file->private_data = ctx; + file->f_mapping = inode->i_mapping; + ctx->signal1 = inode->i_mapping; + return nonseekable_open(inode, file); +} + static ssize_t spufs_signal1_read(struct file *file, char __user *buf, size_t len, loff_t *pos) { @@ -543,12 +657,50 @@ static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, return 4; } +#ifdef CONFIG_SPUFS_MMAP +static struct page *spufs_signal1_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return spufs_ps_nopage(vma, address, type, 0x14000); +} + +static struct vm_operations_struct spufs_signal1_mmap_vmops = { + .nopage = spufs_signal1_mmap_nopage, +}; + +static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) + | _PAGE_NO_CACHE); + + vma->vm_ops = &spufs_signal1_mmap_vmops; + return 0; +} +#endif + static struct file_operations spufs_signal1_fops = { - .open = spufs_pipe_open, + .open = spufs_signal1_open, .read = spufs_signal1_read, .write = spufs_signal1_write, +#ifdef CONFIG_SPUFS_MMAP + .mmap = spufs_signal1_mmap, +#endif }; +static int spufs_signal2_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + file->private_data = ctx; + file->f_mapping = inode->i_mapping; + ctx->signal2 = inode->i_mapping; + return nonseekable_open(inode, file); +} + static ssize_t spufs_signal2_read(struct file *file, char __user *buf, size_t len, loff_t *pos) { @@ -591,10 +743,39 @@ static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, return 4; } +#ifdef CONFIG_SPUFS_MMAP +static struct page *spufs_signal2_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return spufs_ps_nopage(vma, address, type, 0x1c000); +} + +static struct vm_operations_struct spufs_signal2_mmap_vmops = { + .nopage = spufs_signal2_mmap_nopage, +}; + +static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + /* FIXME: */ + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) + | _PAGE_NO_CACHE); + + vma->vm_ops = &spufs_signal2_mmap_vmops; + return 0; +} +#endif + static struct file_operations spufs_signal2_fops = { - .open = spufs_pipe_open, + .open = spufs_signal2_open, .read = spufs_signal2_read, .write = spufs_signal2_write, +#ifdef CONFIG_SPUFS_MMAP + .mmap = spufs_signal2_mmap, +#endif }; static void spufs_signal1_type_set(void *data, u64 val) @@ -643,6 +824,38 @@ static u64 spufs_signal2_type_get(void *data) DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, spufs_signal2_type_set, "%llu"); +#ifdef CONFIG_SPUFS_MMAP +static struct page *spufs_mfc_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return spufs_ps_nopage(vma, address, type, 0x3000); +} + +static struct vm_operations_struct spufs_mfc_mmap_vmops = { + .nopage = spufs_mfc_mmap_nopage, +}; + +/* + * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. + * Mapping this area requires that the application have CAP_SYS_RAWIO, + * as these registers require special care when read/writing. + */ +static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) + | _PAGE_NO_CACHE); + + vma->vm_ops = &spufs_mfc_mmap_vmops; + return 0; +} +#endif static int spufs_mfc_open(struct inode *inode, struct file *file) { @@ -932,6 +1145,9 @@ static struct file_operations spufs_mfc_fops = { .flush = spufs_mfc_flush, .fsync = spufs_mfc_fsync, .fasync = spufs_mfc_fasync, +#ifdef CONFIG_SPUFS_MMAP + .mmap = spufs_mfc_mmap, +#endif }; static void spufs_npc_set(void *data, u64 val) @@ -1077,6 +1293,7 @@ struct tree_descr spufs_dir_contents[] = { { "signal1_type", &spufs_signal1_type, 0666, }, { "signal2_type", &spufs_signal2_type, 0666, }, { "mfc", &spufs_mfc_fops, 0666, }, + { "cntl", &spufs_cntl_fops, 0666, }, { "npc", &spufs_npc_ops, 0666, }, { "fpcr", &spufs_fpcr_fops, 0666, }, { "decr", &spufs_decr_ops, 0666, }, diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index b3962c3a0348..dc06305eecf5 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -241,7 +241,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) inode->i_gid = dir->i_gid; inode->i_mode &= S_ISGID; } - ctx = alloc_spu_context(inode->i_mapping); + ctx = alloc_spu_context(); SPUFS_I(inode)->i_ctx = ctx; if (!ctx) goto out_iput; diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 57d687ca3f03..4485738e2102 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -43,7 +43,11 @@ struct spu_context { struct spu *spu; /* pointer to a physical SPU */ struct spu_state csa; /* SPU context save area. */ spinlock_t mmio_lock; /* protects mmio access */ - struct address_space *local_store;/* local store backing store */ + struct address_space *local_store; /* local store mapping. */ + struct address_space *mfc; /* 'mfc' area mappings. */ + struct address_space *cntl; /* 'control' area mappings. */ + struct address_space *signal1; /* 'signal1' area mappings. */ + struct address_space *signal2; /* 'signal2' area mappings. */ enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state; struct rw_semaphore state_sema; @@ -125,7 +129,7 @@ long spufs_create_thread(struct nameidata *nd, extern struct file_operations spufs_context_fops; /* context management */ -struct spu_context * alloc_spu_context(struct address_space *local_store); +struct spu_context * alloc_spu_context(void); void destroy_spu_context(struct kref *kref); struct spu_context * get_spu_context(struct spu_context *ctx); int put_spu_context(struct spu_context *ctx); diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h index 8564b8234069..f431d8b0b651 100644 --- a/include/asm-powerpc/spu.h +++ b/include/asm-powerpc/spu.h @@ -110,6 +110,7 @@ struct spu { char *name; unsigned long local_store_phys; u8 *local_store; + unsigned long problem_phys; struct spu_problem __iomem *problem; struct spu_priv1 __iomem *priv1; struct spu_priv2 __iomem *priv2; -- cgit v1.2.3 From 4df20460a3ff0d60280738b094945c56cb5567a5 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sat, 25 Mar 2006 17:25:17 +1100 Subject: [PATCH] powerpc: Allow non zero boot cpuids We currently have a hack to flip the boot cpu and its secondary thread to logical cpuid 0 and 1. This means the logical - physical mapping will differ depending on which cpu is boot cpu. This is most apparent on kexec, where we might kexec on any cpu and therefore change the mapping from boot to boot. The patch below does a first pass early on to work out the logical cpuid of the boot thread. We then fix up some paca structures to match. Ive also removed the boot_cpuid_phys variable for ppc64, to be consistent we use get_hard_smp_processor_id(boot_cpuid) everywhere. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/head_64.S | 26 +++++------ arch/powerpc/kernel/paca.c | 21 ++------- arch/powerpc/kernel/prom.c | 83 +++++++++++++++++++++++------------ arch/powerpc/kernel/setup-common.c | 16 +------ arch/powerpc/kernel/setup_64.c | 12 +++-- arch/powerpc/platforms/pseries/xics.c | 2 +- include/asm-powerpc/paca.h | 2 + include/asm-powerpc/smp.h | 2 +- 8 files changed, 86 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 7e7f7d243304..a5ae04a57c78 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -1851,21 +1851,6 @@ _STATIC(start_here_multiplatform) bl .__save_cpu_setup sync - /* Setup a valid physical PACA pointer in SPRG3 for early_setup - * note that boot_cpuid can always be 0 nowadays since there is - * nowhere it can be initialized differently before we reach this - * code - */ - LOAD_REG_IMMEDIATE(r27, boot_cpuid) - add r27,r27,r26 - lwz r27,0(r27) - - LOAD_REG_IMMEDIATE(r24, paca) /* Get base vaddr of paca array */ - mulli r13,r27,PACA_SIZE /* Calculate vaddr of right paca */ - add r13,r13,r24 /* for this processor. */ - add r13,r13,r26 /* convert to physical addr */ - mtspr SPRN_SPRG3,r13 - /* Do very early kernel initializations, including initial hash table, * stab and slb setup before we turn on relocation. */ @@ -1934,6 +1919,17 @@ _STATIC(start_here_common) /* Not reached */ BUG_OPCODE +/* Put the paca pointer into r13 and SPRG3 */ +_GLOBAL(setup_boot_paca) + LOAD_REG_IMMEDIATE(r3, boot_cpuid) + lwz r3,0(r3) + LOAD_REG_IMMEDIATE(r4, paca) /* Get base vaddr of paca array */ + mulli r3,r3,PACA_SIZE /* Calculate vaddr of right paca */ + add r13,r3,r4 /* for this processor. */ + mtspr SPRN_SPRG3,r13 + + blr + /* * We put a few things here that have to be page-aligned. * This stuff goes at the beginning of the bss, which is page-aligned. diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index 5d1b708086bd..f505a8827e3e 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c @@ -56,14 +56,11 @@ struct lppaca lppaca[] = { * processors. The processor VPD array needs one entry per physical * processor (not thread). */ -#define PACA_INIT_COMMON(number, start, asrr, asrv) \ +#define PACA_INIT_COMMON(number) \ .lppaca_ptr = &lppaca[number], \ .lock_token = 0x8000, \ .paca_index = (number), /* Paca Index */ \ .kernel_toc = (unsigned long)(&__toc_start) + 0x8000UL, \ - .stab_real = (asrr), /* Real pointer to segment table */ \ - .stab_addr = (asrv), /* Virt pointer to segment table */ \ - .cpu_start = (start), /* Processor start */ \ .hw_cpu_id = 0xffff, #ifdef CONFIG_PPC_ISERIES @@ -72,30 +69,20 @@ struct lppaca lppaca[] = { #define PACA_INIT(number) \ { \ - PACA_INIT_COMMON(number, 0, 0, 0) \ - PACA_INIT_ISERIES(number) \ -} - -#define BOOTCPU_PACA_INIT(number) \ -{ \ - PACA_INIT_COMMON(number, 1, 0, (u64)&initial_stab) \ + PACA_INIT_COMMON(number) \ PACA_INIT_ISERIES(number) \ } #else #define PACA_INIT(number) \ { \ - PACA_INIT_COMMON(number, 0, 0, 0) \ + PACA_INIT_COMMON(number) \ } -#define BOOTCPU_PACA_INIT(number) \ -{ \ - PACA_INIT_COMMON(number, 1, STAB0_PHYS_ADDR, (u64)&initial_stab) \ -} #endif struct paca_struct paca[] = { - BOOTCPU_PACA_INIT(0), + PACA_INIT(0), #if NR_CPUS > 1 PACA_INIT( 1), PACA_INIT( 2), PACA_INIT( 3), #if NR_CPUS > 4 diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index d63cd562d9d5..5a24415a2e3c 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -854,35 +854,70 @@ void __init unflatten_device_tree(void) DBG(" <- unflatten_device_tree()\n"); } - static int __init early_init_dt_scan_cpus(unsigned long node, - const char *uname, int depth, void *data) + const char *uname, int depth, + void *data) { - u32 *prop; - unsigned long size; - char *type = of_get_flat_dt_prop(node, "device_type", &size); + static int logical_cpuid = 0; + char *type = of_get_flat_dt_prop(node, "device_type", NULL); + u32 *prop, *intserv; + int i, nthreads; + unsigned long len; + int found = 0; /* We are scanning "cpu" nodes only */ if (type == NULL || strcmp(type, "cpu") != 0) return 0; - boot_cpuid = 0; - boot_cpuid_phys = 0; - if (initial_boot_params && initial_boot_params->version >= 2) { - /* version 2 of the kexec param format adds the phys cpuid - * of booted proc. - */ - boot_cpuid_phys = initial_boot_params->boot_cpuid_phys; + /* Get physical cpuid */ + intserv = of_get_flat_dt_prop(node, "ibm,ppc-interrupt-server#s", &len); + if (intserv) { + nthreads = len / sizeof(int); } else { - /* Check if it's the boot-cpu, set it's hw index now */ - if (of_get_flat_dt_prop(node, + intserv = of_get_flat_dt_prop(node, "reg", NULL); + nthreads = 1; + } + + /* + * Now see if any of these threads match our boot cpu. + * NOTE: This must match the parsing done in smp_setup_cpu_maps. + */ + for (i = 0; i < nthreads; i++) { + /* + * version 2 of the kexec param format adds the phys cpuid of + * booted proc. + */ + if (initial_boot_params && initial_boot_params->version >= 2) { + if (intserv[i] == + initial_boot_params->boot_cpuid_phys) { + found = 1; + break; + } + } else { + /* + * Check if it's the boot-cpu, set it's hw index now, + * unfortunately this format did not support booting + * off secondary threads. + */ + if (of_get_flat_dt_prop(node, "linux,boot-cpu", NULL) != NULL) { - prop = of_get_flat_dt_prop(node, "reg", NULL); - if (prop != NULL) - boot_cpuid_phys = *prop; + found = 1; + break; + } } + +#ifdef CONFIG_SMP + /* logical cpu id is always 0 on UP kernels */ + logical_cpuid++; +#endif + } + + if (found) { + DBG("boot cpu: logical %d physical %d\n", logical_cpuid, + intserv[i]); + boot_cpuid = logical_cpuid; + set_hard_smp_processor_id(boot_cpuid, intserv[i]); } - set_hard_smp_processor_id(0, boot_cpuid_phys); #ifdef CONFIG_ALTIVEC /* Check if we have a VMX and eventually update CPU features */ @@ -901,16 +936,10 @@ static int __init early_init_dt_scan_cpus(unsigned long node, #endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_PPC_PSERIES - /* - * Check for an SMT capable CPU and set the CPU feature. We do - * this by looking at the size of the ibm,ppc-interrupt-server#s - * property - */ - prop = (u32 *)of_get_flat_dt_prop(node, "ibm,ppc-interrupt-server#s", - &size); - cur_cpu_spec->cpu_features &= ~CPU_FTR_SMT; - if (prop && ((size / sizeof(u32)) > 1)) + if (nthreads > 1) cur_cpu_spec->cpu_features |= CPU_FTR_SMT; + else + cur_cpu_spec->cpu_features &= ~CPU_FTR_SMT; #endif return 0; diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index c1d62bf11f29..b17630ad4ac7 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -352,12 +352,13 @@ void __init check_for_initrd(void) * must be called before using this. * * While we're here, we may as well set the "physical" cpu ids in the paca. + * + * NOTE: This must match the parsing done in early_init_dt_scan_cpus. */ void __init smp_setup_cpu_maps(void) { struct device_node *dn = NULL; int cpu = 0; - int swap_cpuid = 0; while ((dn = of_find_node_by_type(dn, "cpu")) && cpu < NR_CPUS) { int *intserv; @@ -376,24 +377,11 @@ void __init smp_setup_cpu_maps(void) for (j = 0; j < nthreads && cpu < NR_CPUS; j++) { cpu_set(cpu, cpu_present_map); set_hard_smp_processor_id(cpu, intserv[j]); - - if (intserv[j] == boot_cpuid_phys) - swap_cpuid = cpu; cpu_set(cpu, cpu_possible_map); cpu++; } } - /* Swap CPU id 0 with boot_cpuid_phys, so we can always assume that - * boot cpu is logical 0. - */ - if (boot_cpuid_phys != get_hard_smp_processor_id(0)) { - u32 tmp; - tmp = get_hard_smp_processor_id(0); - set_hard_smp_processor_id(0, boot_cpuid_phys); - set_hard_smp_processor_id(swap_cpuid, tmp); - } - #ifdef CONFIG_PPC64 /* * On pSeries LPAR, we need to know how many cpus diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 2f3fdad35594..6c9b093c23a5 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -73,7 +73,6 @@ int have_of = 1; int boot_cpuid = 0; -int boot_cpuid_phys = 0; dev_t boot_dev; u64 ppc64_pft_size; @@ -208,7 +207,6 @@ static struct machdep_calls __initdata *machines[] = { void __init early_setup(unsigned long dt_ptr) { - struct paca_struct *lpaca = get_paca(); static struct machdep_calls **mach; /* Enable early debugging if any specified (see udbg.h) */ @@ -223,6 +221,14 @@ void __init early_setup(unsigned long dt_ptr) */ early_init_devtree(__va(dt_ptr)); + /* Now we know the logical id of our boot cpu, setup the paca. */ + setup_boot_paca(); + + /* Fix up paca fields required for the boot cpu */ + get_paca()->cpu_start = 1; + get_paca()->stab_real = __pa((u64)&initial_stab); + get_paca()->stab_addr = (u64)&initial_stab; + /* * Iterate all ppc_md structures until we find the proper * one for the current machine type @@ -260,7 +266,7 @@ void __init early_setup(unsigned long dt_ptr) if (cpu_has_feature(CPU_FTR_SLB)) slb_initialize(); else - stab_initialize(lpaca->stab_real); + stab_initialize(get_paca()->stab_real); } DBG(" <- early_setup()\n"); diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index eb86cdb9b802..c60d3ff25a2f 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -500,7 +500,7 @@ nextnode: np; np = of_find_node_by_type(np, "cpu")) { ireg = (uint *)get_property(np, "reg", &ilen); - if (ireg && ireg[0] == boot_cpuid_phys) { + if (ireg && ireg[0] == get_hard_smp_processor_id(boot_cpuid)) { ireg = (uint *)get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); i = ilen / sizeof(int); diff --git a/include/asm-powerpc/paca.h b/include/asm-powerpc/paca.h index 4465b95ebef0..706325f99a84 100644 --- a/include/asm-powerpc/paca.h +++ b/include/asm-powerpc/paca.h @@ -105,5 +105,7 @@ struct paca_struct { extern struct paca_struct paca[]; +void setup_boot_paca(void); + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PACA_H */ diff --git a/include/asm-powerpc/smp.h b/include/asm-powerpc/smp.h index 98581e5a8279..4a716f707cf6 100644 --- a/include/asm-powerpc/smp.h +++ b/include/asm-powerpc/smp.h @@ -29,7 +29,6 @@ #endif extern int boot_cpuid; -extern int boot_cpuid_phys; extern void cpu_die(void); @@ -99,6 +98,7 @@ extern void smp_release_cpus(void); #else /* 32-bit */ #ifndef CONFIG_SMP +extern int boot_cpuid_phys; #define get_hard_smp_processor_id(cpu) boot_cpuid_phys #define set_hard_smp_processor_id(cpu, phys) #endif -- cgit v1.2.3 From 5931c4350059ce9bd5fe398b628c478753a11e44 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sun, 26 Mar 2006 13:37:07 +0200 Subject: [PATCH] ppc32: Adds support for the PCI hostbridge in MPC5200B ppc32: Adds support for the PCI hostbridge in MPC5200B Signed-off-by: John Rigby Signed-off-by: Sylvain Munaut Signed-off-by: Paul Mackerras --- arch/ppc/syslib/mpc52xx_pci.c | 3 ++- include/linux/pci_ids.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/ppc/syslib/mpc52xx_pci.c b/arch/ppc/syslib/mpc52xx_pci.c index 9ec525f9fe98..5a5a7a9cd248 100644 --- a/arch/ppc/syslib/mpc52xx_pci.c +++ b/arch/ppc/syslib/mpc52xx_pci.c @@ -225,7 +225,8 @@ mpc52xx_pci_fixup_resources(struct pci_dev *dev) /* The PCI Host bridge of MPC52xx has a prefetch memory resource fixed to 1Gb. Doesn't fit in the resource system so we remove it */ if ( (dev->vendor == PCI_VENDOR_ID_MOTOROLA) && - (dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200) ) { + ( dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200 + || dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200B) ) { struct resource *res = &dev->resource[1]; res->start = res->end = res->flags = 0; } diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 6f080ae59286..72d1b678e0e9 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -772,6 +772,7 @@ #define PCI_DEVICE_ID_MOTOROLA_HAWK 0x4803 #define PCI_DEVICE_ID_MOTOROLA_HARRIER 0x480b #define PCI_DEVICE_ID_MOTOROLA_MPC5200 0x5803 +#define PCI_DEVICE_ID_MOTOROLA_MPC5200B 0x5809 #define PCI_VENDOR_ID_PROMISE 0x105a #define PCI_DEVICE_ID_PROMISE_20265 0x0d30 -- cgit v1.2.3 From 1f5e3b028c5b592b5a792a390c78d609219aebfd Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sun, 26 Mar 2006 13:38:09 +0200 Subject: [PATCH] ppc32: Reorganize and complete MPC52xx initial cpu setup ppc32: Reorganize and complete MPC52xx initial cpu setup This patch splits up the CPU setup into a generic part and a platform specific part. We also add a few missing init at the same time. Signed-off-by: Sylvain Munaut Signed-off-by: Paul Mackerras --- arch/ppc/platforms/lite5200.c | 38 +++++++++----------------------- arch/ppc/syslib/mpc52xx_setup.c | 48 +++++++++++++++++++++++++++++++++++++++++ include/asm-ppc/mpc52xx.h | 4 ++++ 3 files changed, 62 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/arch/ppc/platforms/lite5200.c b/arch/ppc/platforms/lite5200.c index d91efe1dc2d1..fecbe9adc9e0 100644 --- a/arch/ppc/platforms/lite5200.c +++ b/arch/ppc/platforms/lite5200.c @@ -36,8 +36,6 @@ #include #include -#include - extern int powersave_nap; @@ -99,34 +97,23 @@ lite5200_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) static void __init lite5200_setup_cpu(void) { - struct mpc52xx_cdm __iomem *cdm; struct mpc52xx_gpio __iomem *gpio; struct mpc52xx_intr __iomem *intr; - struct mpc52xx_xlb __iomem *xlb; u32 port_config; u32 intr_ctrl; /* Map zones */ - cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE); gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE); - xlb = ioremap(MPC52xx_PA(MPC52xx_XLB_OFFSET), MPC52xx_XLB_SIZE); intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE); - if (!cdm || !gpio || !xlb || !intr) { - printk("lite5200.c: Error while mapping CDM/GPIO/XLB/INTR during" - "lite5200_setup_cpu\n"); + if (!gpio || !intr) { + printk(KERN_ERR __FILE__ ": " + "Error while mapping GPIO/INTR during " + "lite5200_setup_cpu\n"); goto unmap_regs; } - /* Use internal 48 Mhz */ - out_8(&cdm->ext_48mhz_en, 0x00); - out_8(&cdm->fd_enable, 0x01); - if (in_be32(&cdm->rstcfg) & 0x40) /* Assumes 33Mhz clock */ - out_be16(&cdm->fd_counters, 0x0001); - else - out_be16(&cdm->fd_counters, 0x5555); - /* Get port mux config */ port_config = in_be32(&gpio->port_config); @@ -137,17 +124,13 @@ lite5200_setup_cpu(void) port_config &= ~0x00007000; /* Differential mode - USB1 only */ port_config |= 0x00001000; + /* ATA CS is on csb_4/5 */ + port_config &= ~0x03000000; + port_config |= 0x01000000; + /* Commit port config */ out_be32(&gpio->port_config, port_config); - /* Configure the XLB Arbiter */ - out_be32(&xlb->master_pri_enable, 0xff); - out_be32(&xlb->master_priority, 0x11111111); - - /* Enable ram snooping for 1GB window */ - out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_SNOOP); - out_be32(&xlb->snoop_window, MPC52xx_PCI_TARGET_MEM | 0x1d); - /* IRQ[0-3] setup */ intr_ctrl = in_be32(&intr->ctrl); intr_ctrl &= ~0x00ff0000; @@ -163,9 +146,7 @@ lite5200_setup_cpu(void) /* Unmap reg zone */ unmap_regs: - if (cdm) iounmap(cdm); if (gpio) iounmap(gpio); - if (xlb) iounmap(xlb); if (intr) iounmap(intr); } @@ -173,7 +154,8 @@ static void __init lite5200_setup_arch(void) { /* CPU & Port mux setup */ - lite5200_setup_cpu(); + mpc52xx_setup_cpu(); /* Generic */ + lite5200_setup_cpu(); /* Platform specific */ #ifdef CONFIG_PCI /* PCI Bridge setup */ diff --git a/arch/ppc/syslib/mpc52xx_setup.c b/arch/ppc/syslib/mpc52xx_setup.c index 2ee48ce0a517..ee6379bb415e 100644 --- a/arch/ppc/syslib/mpc52xx_setup.c +++ b/arch/ppc/syslib/mpc52xx_setup.c @@ -24,6 +24,8 @@ #include #include +#include + extern bd_t __res; static int core_mult[] = { /* CPU Frequency multiplier, taken */ @@ -216,6 +218,52 @@ mpc52xx_calibrate_decr(void) tb_to_us = mulhwu_scale_factor(xlbfreq / divisor, 1000000); } + +void __init +mpc52xx_setup_cpu(void) +{ + struct mpc52xx_cdm __iomem *cdm; + struct mpc52xx_xlb __iomem *xlb; + + /* Map zones */ + cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE); + xlb = ioremap(MPC52xx_PA(MPC52xx_XLB_OFFSET), MPC52xx_XLB_SIZE); + + if (!cdm || !xlb) { + printk(KERN_ERR __FILE__ ": " + "Error while mapping CDM/XLB during " + "mpc52xx_setup_cpu\n"); + goto unmap_regs; + } + + /* Use internal 48 Mhz */ + out_8(&cdm->ext_48mhz_en, 0x00); + out_8(&cdm->fd_enable, 0x01); + if (in_be32(&cdm->rstcfg) & 0x40) /* Assumes 33Mhz clock */ + out_be16(&cdm->fd_counters, 0x0001); + else + out_be16(&cdm->fd_counters, 0x5555); + + /* Configure the XLB Arbiter priorities */ + out_be32(&xlb->master_pri_enable, 0xff); + out_be32(&xlb->master_priority, 0x11111111); + + /* Enable ram snooping for 1GB window */ + out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_SNOOP); + out_be32(&xlb->snoop_window, MPC52xx_PCI_TARGET_MEM | 0x1d); + + /* Disable XLB pipelining */ + /* (cfr errate 292. We could do this only just before ATA PIO + transaction and re-enable it after ...) */ + out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_PLDIS); + + /* Unmap reg zone */ +unmap_regs: + if (cdm) iounmap(cdm); + if (xlb) iounmap(xlb); +} + + int mpc52xx_match_psc_function(int psc_idx, const char *func) { struct mpc52xx_psc_func *cf = mpc52xx_psc_functions; diff --git a/include/asm-ppc/mpc52xx.h b/include/asm-ppc/mpc52xx.h index 6167f74635f7..7e9842805a28 100644 --- a/include/asm-ppc/mpc52xx.h +++ b/include/asm-ppc/mpc52xx.h @@ -355,6 +355,7 @@ struct mpc52xx_xlb { u32 snoop_window; /* XLB + 0x70 */ }; +#define MPC52xx_XLB_CFG_PLDIS (1 << 31) #define MPC52xx_XLB_CFG_SNOOP (1 << 15) /* Clock Distribution control */ @@ -427,6 +428,9 @@ extern void mpc52xx_calibrate_decr(void); extern void mpc52xx_find_bridges(void); +extern void mpc52xx_setup_cpu(void); + + /* Matching of PSC function */ struct mpc52xx_psc_func { -- cgit v1.2.3 From a0652fc9a28c3ef8cd59264bfcb089c44d1b0e06 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 27 Mar 2006 15:03:03 +1100 Subject: powerpc: Unify the 32 and 64 bit idle loops This unifies the 32-bit (ARCH=ppc and ARCH=powerpc) and 64-bit idle loops. It brings over the concept of having a ppc_md.power_save function from 32-bit to ARCH=powerpc, which lets us get rid of native_idle(). With this we will also be able to simplify the idle handling for pSeries and cell. Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/Makefile | 6 +- arch/powerpc/kernel/entry_32.S | 8 +- arch/powerpc/kernel/idle.c | 122 +++++++++++++++++ arch/powerpc/kernel/idle_64.c | 121 ----------------- arch/powerpc/kernel/idle_6xx.S | 15 -- arch/powerpc/kernel/idle_power4.S | 15 -- arch/powerpc/kernel/setup_32.c | 7 +- arch/powerpc/kernel/setup_64.c | 6 - arch/powerpc/platforms/maple/setup.c | 2 +- arch/powerpc/platforms/powermac/setup.c | 2 +- arch/ppc/Makefile | 2 - arch/ppc/kernel/Makefile | 4 +- arch/ppc/kernel/entry.S | 8 +- arch/ppc/kernel/idle.c | 112 --------------- arch/ppc/kernel/idle_6xx.S | 233 -------------------------------- arch/ppc/kernel/idle_power4.S | 91 ------------- include/asm-powerpc/machdep.h | 13 +- include/asm-powerpc/reg.h | 4 + include/asm-ppc/machdep.h | 2 +- 19 files changed, 156 insertions(+), 617 deletions(-) create mode 100644 arch/powerpc/kernel/idle.c delete mode 100644 arch/powerpc/kernel/idle_64.c delete mode 100644 arch/ppc/kernel/idle.c delete mode 100644 arch/ppc/kernel/idle_6xx.S delete mode 100644 arch/ppc/kernel/idle_power4.S (limited to 'include') diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 80e9fe2632b8..f2c47e907037 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -12,12 +12,12 @@ endif obj-y := semaphore.o cputable.o ptrace.o syscalls.o \ irq.o align.o signal_32.o pmc.o vdso.o \ - init_task.o process.o systbl.o + init_task.o process.o systbl.o idle.o obj-y += vdso32/ obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \ signal_64.o ptrace32.o \ paca.o cpu_setup_power4.o \ - firmware.o sysfs.o idle_64.o + firmware.o sysfs.o obj-$(CONFIG_PPC64) += vdso64/ obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o obj-$(CONFIG_POWER4) += idle_power4.o @@ -34,6 +34,7 @@ obj-$(CONFIG_IBMEBUS) += ibmebus.o obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o obj64-$(CONFIG_PPC_MULTIPLATFORM) += nvram_64.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o +obj-$(CONFIG_6xx) += idle_6xx.o ifeq ($(CONFIG_PPC_MERGE),y) @@ -51,7 +52,6 @@ obj-$(CONFIG_PPC64) += misc_64.o dma_64.o iommu.o obj-$(CONFIG_PPC_MULTIPLATFORM) += prom_init.o obj-$(CONFIG_MODULES) += ppc_ksyms.o obj-$(CONFIG_BOOTX_TEXT) += btext.o -obj-$(CONFIG_6xx) += idle_6xx.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 4827ca1ec89b..b3a979467225 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -135,10 +135,10 @@ transfer_to_handler: mfspr r11,SPRN_HID0 mtcr r11 BEGIN_FTR_SECTION - bt- 8,power_save_6xx_restore /* Check DOZE */ + bt- 8,4f /* Check DOZE */ END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) BEGIN_FTR_SECTION - bt- 9,power_save_6xx_restore /* Check NAP */ + bt- 9,4f /* Check NAP */ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) #endif /* CONFIG_6xx */ .globl transfer_to_handler_cont @@ -157,6 +157,10 @@ transfer_to_handler_cont: SYNC RFI /* jump to handler, enable MMU */ +#ifdef CONFIG_6xx +4: b power_save_6xx_restore +#endif + /* * On kernel stack overflow, load up an initial stack pointer * and call StackOverflow(regs), which should not return. diff --git a/arch/powerpc/kernel/idle.c b/arch/powerpc/kernel/idle.c new file mode 100644 index 000000000000..e9f321d74d85 --- /dev/null +++ b/arch/powerpc/kernel/idle.c @@ -0,0 +1,122 @@ +/* + * Idle daemon for PowerPC. Idle daemon will handle any action + * that needs to be taken when the system becomes idle. + * + * Originally written by Cort Dougan (cort@cs.nmt.edu). + * Subsequent 32-bit hacking by Tom Rini, Armin Kuster, + * Paul Mackerras and others. + * + * iSeries supported added by Mike Corrigan + * + * Additional shared processor, SMT, and firmware support + * Copyright (c) 2003 Dave Engebretsen + * + * 32-bit and 64-bit versions merged by Paul Mackerras + * + * 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 + +#ifdef CONFIG_HOTPLUG_CPU +#define cpu_should_die() (cpu_is_offline(smp_processor_id()) && \ + system_state == SYSTEM_RUNNING) +#else +#define cpu_should_die() 0 +#endif + +/* + * The body of the idle task. + */ +void cpu_idle(void) +{ + if (ppc_md.idle_loop) + ppc_md.idle_loop(); /* doesn't return */ + + set_thread_flag(TIF_POLLING_NRFLAG); + while (1) { + ppc64_runlatch_off(); + + while (!need_resched() && !cpu_should_die()) { + if (ppc_md.power_save) { + clear_thread_flag(TIF_POLLING_NRFLAG); + /* + * smp_mb is so clearing of TIF_POLLING_NRFLAG + * is ordered w.r.t. need_resched() test. + */ + smp_mb(); + local_irq_disable(); + + /* check again after disabling irqs */ + if (!need_resched() && !cpu_should_die()) + ppc_md.power_save(); + + local_irq_enable(); + set_thread_flag(TIF_POLLING_NRFLAG); + + } else { + /* + * Go into low thread priority and possibly + * low power mode. + */ + HMT_low(); + HMT_very_low(); + } + } + + HMT_medium(); + ppc64_runlatch_on(); + if (cpu_should_die()) + cpu_die(); + preempt_enable_no_resched(); + schedule(); + preempt_disable(); + } +} + +int powersave_nap; + +#ifdef CONFIG_SYSCTL +/* + * Register the sysctl to set/clear powersave_nap. + */ +static ctl_table powersave_nap_ctl_table[]={ + { + .ctl_name = KERN_PPC_POWERSAVE_NAP, + .procname = "powersave-nap", + .data = &powersave_nap, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { 0, }, +}; +static ctl_table powersave_nap_sysctl_root[] = { + { 1, "kernel", NULL, 0, 0755, powersave_nap_ctl_table, }, + { 0,}, +}; + +static int __init +register_powersave_nap_sysctl(void) +{ + register_sysctl_table(powersave_nap_sysctl_root, 0); + + return 0; +} +__initcall(register_powersave_nap_sysctl); +#endif diff --git a/arch/powerpc/kernel/idle_64.c b/arch/powerpc/kernel/idle_64.c deleted file mode 100644 index b879d3057ef8..000000000000 --- a/arch/powerpc/kernel/idle_64.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Idle daemon for PowerPC. Idle daemon will handle any action - * that needs to be taken when the system becomes idle. - * - * Originally Written by Cort Dougan (cort@cs.nmt.edu) - * - * iSeries supported added by Mike Corrigan - * - * Additional shared processor, SMT, and firmware support - * Copyright (c) 2003 Dave Engebretsen - * - * 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 - -extern void power4_idle(void); - -void default_idle(void) -{ - unsigned int cpu = smp_processor_id(); - set_thread_flag(TIF_POLLING_NRFLAG); - - while (1) { - if (!need_resched()) { - while (!need_resched() && !cpu_is_offline(cpu)) { - ppc64_runlatch_off(); - - /* - * Go into low thread priority and possibly - * low power mode. - */ - HMT_low(); - HMT_very_low(); - } - - HMT_medium(); - } - - ppc64_runlatch_on(); - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING) - cpu_die(); - } -} - -void native_idle(void) -{ - while (1) { - ppc64_runlatch_off(); - - if (!need_resched()) - power4_idle(); - - if (need_resched()) { - ppc64_runlatch_on(); - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - } - - if (cpu_is_offline(smp_processor_id()) && - system_state == SYSTEM_RUNNING) - cpu_die(); - } -} - -void cpu_idle(void) -{ - BUG_ON(NULL == ppc_md.idle_loop); - ppc_md.idle_loop(); -} - -int powersave_nap; - -#ifdef CONFIG_SYSCTL -/* - * Register the sysctl to set/clear powersave_nap. - */ -static ctl_table powersave_nap_ctl_table[]={ - { - .ctl_name = KERN_PPC_POWERSAVE_NAP, - .procname = "powersave-nap", - .data = &powersave_nap, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { 0, }, -}; -static ctl_table powersave_nap_sysctl_root[] = { - { 1, "kernel", NULL, 0, 0755, powersave_nap_ctl_table, }, - { 0,}, -}; - -static int __init -register_powersave_nap_sysctl(void) -{ - register_sysctl_table(powersave_nap_sysctl_root, 0); - - return 0; -} -__initcall(register_powersave_nap_sysctl); -#endif diff --git a/arch/powerpc/kernel/idle_6xx.S b/arch/powerpc/kernel/idle_6xx.S index 444fdcc769f1..1647ea361ef7 100644 --- a/arch/powerpc/kernel/idle_6xx.S +++ b/arch/powerpc/kernel/idle_6xx.S @@ -87,19 +87,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) cmpwi 0,r3,0 beqlr - /* Clear MSR:EE */ - mfmsr r7 - rlwinm r0,r7,0,17,15 - mtmsr r0 - - /* Check current_thread_info()->flags */ - rlwinm r4,r1,0,0,18 - lwz r4,TI_FLAGS(r4) - andi. r0,r4,_TIF_NEED_RESCHED - beq 1f - mtmsr r7 /* out of line this ? */ - blr -1: /* Some pre-nap cleanups needed on some CPUs */ andis. r0,r3,HID0_NAP@h beq 2f @@ -220,8 +207,6 @@ _GLOBAL(nap_save_msscr0) _GLOBAL(nap_save_hid1) .space 4*NR_CPUS -_GLOBAL(powersave_nap) - .long 0 _GLOBAL(powersave_lowspeed) .long 0 diff --git a/arch/powerpc/kernel/idle_power4.S b/arch/powerpc/kernel/idle_power4.S index c16b4afab582..692cf2ebe0f4 100644 --- a/arch/powerpc/kernel/idle_power4.S +++ b/arch/powerpc/kernel/idle_power4.S @@ -49,21 +49,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) cmpwi 0,r4,0 beqlr - /* Clear MSR:EE */ - mfmsr r7 - li r4,0 - ori r4,r4,MSR_EE - andc r0,r7,r4 - mtmsrd r0 - - /* Check current_thread_info()->flags */ - clrrdi r4,r1,THREAD_SHIFT - ld r4,TI_FLAGS(r4) - andi. r0,r4,_TIF_NEED_RESCHED - beq 1f - mtmsrd r7 /* out of line this ? */ - blr -1: /* Go to NAP now */ BEGIN_FTR_SECTION DSSALL diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index 676f894c3380..e39f830317a8 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -53,9 +53,6 @@ extern void platform_init(void); extern void bootx_init(unsigned long r4, unsigned long phys); -extern void ppc6xx_idle(void); -extern void power4_idle(void); - boot_infos_t *boot_infos; struct ide_machdep_calls ppc_ide_md; @@ -194,7 +191,9 @@ void __init machine_init(unsigned long dt_ptr, unsigned long phys) platform_init(); #ifdef CONFIG_6xx - ppc_md.power_save = ppc6xx_idle; + if (cpu_has_feature(CPU_FTR_CAN_DOZE) || + cpu_has_feature(CPU_FTR_CAN_NAP)) + ppc_md.power_save = ppc6xx_idle; #endif if (ppc_md.progress) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 6c9b093c23a5..5b63a861ef43 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -607,12 +607,6 @@ void __init setup_arch(char **cmdline_p) ppc_md.setup_arch(); - /* Use the default idle loop if the platform hasn't provided one. */ - if (NULL == ppc_md.idle_loop) { - ppc_md.idle_loop = default_idle; - printk(KERN_INFO "Using default idle loop\n"); - } - paging_init(); ppc64_boot_msg(0x15, "Setup Done"); } diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index ec5c1e10c407..137d6063182b 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -290,7 +290,7 @@ struct machdep_calls __initdata maple_md = { .get_rtc_time = maple_get_rtc_time, .calibrate_decr = generic_calibrate_decr, .progress = maple_progress, - .idle_loop = native_idle, + .power_save = power4_idle, #ifdef CONFIG_KEXEC .machine_kexec = default_machine_kexec, .machine_kexec_prepare = default_machine_kexec_prepare, diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 385aab90c4d2..c2696d00672a 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -733,7 +733,7 @@ struct machdep_calls __initdata pmac_md = { .progress = udbg_progress, #ifdef CONFIG_PPC64 .pci_probe_mode = pmac_pci_probe_mode, - .idle_loop = native_idle, + .power_save = power4_idle, .enable_pmcs = power4_enable_pmcs, #ifdef CONFIG_KEXEC .machine_kexec = default_machine_kexec, diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index 9fbdf54ba2be..cde5fa87f455 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -59,8 +59,6 @@ head-$(CONFIG_4xx) := arch/ppc/kernel/head_4xx.o head-$(CONFIG_44x) := arch/ppc/kernel/head_44x.o head-$(CONFIG_FSL_BOOKE) := arch/ppc/kernel/head_fsl_booke.o -head-$(CONFIG_6xx) += arch/ppc/kernel/idle_6xx.o -head-$(CONFIG_POWER4) += arch/ppc/kernel/idle_power4.o head-$(CONFIG_PPC_FPU) += arch/powerpc/kernel/fpu.o core-y += arch/ppc/kernel/ arch/powerpc/kernel/ \ diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index e399bbb969a4..1b2c7458a3d0 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -8,10 +8,9 @@ extra-$(CONFIG_40x) := head_4xx.o extra-$(CONFIG_44x) := head_44x.o extra-$(CONFIG_FSL_BOOKE) := head_fsl_booke.o extra-$(CONFIG_8xx) := head_8xx.o -extra-$(CONFIG_6xx) += idle_6xx.o extra-y += vmlinux.lds -obj-y := entry.o traps.o idle.o time.o misc.o \ +obj-y := entry.o traps.o time.o misc.o \ setup.o \ ppc_htab.o obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o @@ -35,7 +34,6 @@ endif # These are here while we do the architecture merge else -obj-y := idle.o obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj-$(CONFIG_MODULES) += module.o diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S index 3a2815978488..fa8d49789ef1 100644 --- a/arch/ppc/kernel/entry.S +++ b/arch/ppc/kernel/entry.S @@ -135,10 +135,10 @@ transfer_to_handler: mfspr r11,SPRN_HID0 mtcr r11 BEGIN_FTR_SECTION - bt- 8,power_save_6xx_restore /* Check DOZE */ + bt- 8,4f /* Check DOZE */ END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) BEGIN_FTR_SECTION - bt- 9,power_save_6xx_restore /* Check NAP */ + bt- 9,4f /* Check NAP */ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) #endif /* CONFIG_6xx */ .globl transfer_to_handler_cont @@ -157,6 +157,10 @@ transfer_to_handler_cont: SYNC RFI /* jump to handler, enable MMU */ +#ifdef CONFIG_6xx +4: b power_save_6xx_restore +#endif + /* * On kernel stack overflow, load up an initial stack pointer * and call StackOverflow(regs), which should not return. diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c deleted file mode 100644 index 1be3ca5bae40..000000000000 --- a/arch/ppc/kernel/idle.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Idle daemon for PowerPC. Idle daemon will handle any action - * that needs to be taken when the system becomes idle. - * - * Written by Cort Dougan (cort@cs.nmt.edu). Subsequently hacked - * on by Tom Rini, Armin Kuster, Paul Mackerras and others. - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -void default_idle(void) -{ - void (*powersave)(void); - - powersave = ppc_md.power_save; - - if (!need_resched()) { - if (powersave != NULL) - powersave(); -#ifdef CONFIG_SMP - else { - set_thread_flag(TIF_POLLING_NRFLAG); - while (!need_resched() && - !cpu_is_offline(smp_processor_id())) - barrier(); - clear_thread_flag(TIF_POLLING_NRFLAG); - } -#endif - } -} - -/* - * The body of the idle task. - */ -void cpu_idle(void) -{ - int cpu = smp_processor_id(); - - for (;;) { - while (!need_resched()) { - if (ppc_md.idle != NULL) - ppc_md.idle(); - else - default_idle(); - } - - if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING) - cpu_die(); - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - } -} - -#if defined(CONFIG_SYSCTL) && defined(CONFIG_6xx) -/* - * Register the sysctl to set/clear powersave_nap. - */ -extern int powersave_nap; - -static ctl_table powersave_nap_ctl_table[]={ - { - .ctl_name = KERN_PPC_POWERSAVE_NAP, - .procname = "powersave-nap", - .data = &powersave_nap, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { 0, }, -}; -static ctl_table powersave_nap_sysctl_root[] = { - { 1, "kernel", NULL, 0, 0755, powersave_nap_ctl_table, }, - { 0,}, -}; - -static int __init -register_powersave_nap_sysctl(void) -{ - register_sysctl_table(powersave_nap_sysctl_root, 0); - - return 0; -} - -__initcall(register_powersave_nap_sysctl); -#endif diff --git a/arch/ppc/kernel/idle_6xx.S b/arch/ppc/kernel/idle_6xx.S deleted file mode 100644 index 1a2194cf6828..000000000000 --- a/arch/ppc/kernel/idle_6xx.S +++ /dev/null @@ -1,233 +0,0 @@ -/* - * This file contains the power_save function for 6xx & 7xxx CPUs - * rewritten in assembler - * - * Warning ! This code assumes that if your machine has a 750fx - * it will have PLL 1 set to low speed mode (used during NAP/DOZE). - * if this is not the case some additional changes will have to - * be done to check a runtime var (a bit like powersave-nap) - * - * 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 - -#undef DEBUG - - .text - -/* - * Init idle, called at early CPU setup time from head.S for each CPU - * Make sure no rest of NAP mode remains in HID0, save default - * values for some CPU specific registers. Called with r24 - * containing CPU number and r3 reloc offset - */ -_GLOBAL(init_idle_6xx) -BEGIN_FTR_SECTION - mfspr r4,SPRN_HID0 - rlwinm r4,r4,0,10,8 /* Clear NAP */ - mtspr SPRN_HID0, r4 - b 1f -END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) - blr -1: - slwi r5,r24,2 - add r5,r5,r3 -BEGIN_FTR_SECTION - mfspr r4,SPRN_MSSCR0 - addis r6,r5, nap_save_msscr0@ha - stw r4,nap_save_msscr0@l(r6) -END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR) -BEGIN_FTR_SECTION - mfspr r4,SPRN_HID1 - addis r6,r5,nap_save_hid1@ha - stw r4,nap_save_hid1@l(r6) -END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX) - blr - -/* - * Here is the power_save_6xx function. This could eventually be - * split into several functions & changing the function pointer - * depending on the various features. - */ -_GLOBAL(ppc6xx_idle) - /* Check if we can nap or doze, put HID0 mask in r3 - */ - lis r3, 0 -BEGIN_FTR_SECTION - lis r3,HID0_DOZE@h -END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) -BEGIN_FTR_SECTION - /* We must dynamically check for the NAP feature as it - * can be cleared by CPU init after the fixups are done - */ - lis r4,cur_cpu_spec@ha - lwz r4,cur_cpu_spec@l(r4) - lwz r4,CPU_SPEC_FEATURES(r4) - andi. r0,r4,CPU_FTR_CAN_NAP - beq 1f - /* Now check if user or arch enabled NAP mode */ - lis r4,powersave_nap@ha - lwz r4,powersave_nap@l(r4) - cmpwi 0,r4,0 - beq 1f - lis r3,HID0_NAP@h -1: -END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) - cmpwi 0,r3,0 - beqlr - - /* Clear MSR:EE */ - mfmsr r7 - rlwinm r0,r7,0,17,15 - mtmsr r0 - - /* Check current_thread_info()->flags */ - rlwinm r4,r1,0,0,18 - lwz r4,TI_FLAGS(r4) - andi. r0,r4,_TIF_NEED_RESCHED - beq 1f - mtmsr r7 /* out of line this ? */ - blr -1: - /* Some pre-nap cleanups needed on some CPUs */ - andis. r0,r3,HID0_NAP@h - beq 2f -BEGIN_FTR_SECTION - /* Disable L2 prefetch on some 745x and try to ensure - * L2 prefetch engines are idle. As explained by errata - * text, we can't be sure they are, we just hope very hard - * that well be enough (sic !). At least I noticed Apple - * doesn't even bother doing the dcbf's here... - */ - mfspr r4,SPRN_MSSCR0 - rlwinm r4,r4,0,0,29 - sync - mtspr SPRN_MSSCR0,r4 - sync - isync - lis r4,KERNELBASE@h - dcbf 0,r4 - dcbf 0,r4 - dcbf 0,r4 - dcbf 0,r4 -END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR) -#ifdef DEBUG - lis r6,nap_enter_count@ha - lwz r4,nap_enter_count@l(r6) - addi r4,r4,1 - stw r4,nap_enter_count@l(r6) -#endif -2: -BEGIN_FTR_SECTION - /* Go to low speed mode on some 750FX */ - lis r4,powersave_lowspeed@ha - lwz r4,powersave_lowspeed@l(r4) - cmpwi 0,r4,0 - beq 1f - mfspr r4,SPRN_HID1 - oris r4,r4,0x0001 - mtspr SPRN_HID1,r4 -1: -END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX) - - /* Go to NAP or DOZE now */ - mfspr r4,SPRN_HID0 - lis r5,(HID0_NAP|HID0_SLEEP)@h -BEGIN_FTR_SECTION - oris r5,r5,HID0_DOZE@h -END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) - andc r4,r4,r5 - or r4,r4,r3 -BEGIN_FTR_SECTION - oris r4,r4,HID0_DPM@h /* that should be done once for all */ -END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM) - mtspr SPRN_HID0,r4 -BEGIN_FTR_SECTION - DSSALL - sync -END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) - ori r7,r7,MSR_EE /* Could be ommited (already set) */ - oris r7,r7,MSR_POW@h - sync - isync - mtmsr r7 - isync - sync - blr - -/* - * Return from NAP/DOZE mode, restore some CPU specific registers, - * we are called with DR/IR still off and r2 containing physical - * address of current. - */ -_GLOBAL(power_save_6xx_restore) - mfspr r11,SPRN_HID0 - rlwinm. r11,r11,0,10,8 /* Clear NAP & copy NAP bit !state to cr1 EQ */ - cror 4*cr1+eq,4*cr0+eq,4*cr0+eq -BEGIN_FTR_SECTION - rlwinm r11,r11,0,9,7 /* Clear DOZE */ -END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) - mtspr SPRN_HID0, r11 - -#ifdef DEBUG - beq cr1,1f - lis r11,(nap_return_count-KERNELBASE)@ha - lwz r9,nap_return_count@l(r11) - addi r9,r9,1 - stw r9,nap_return_count@l(r11) -1: -#endif - - rlwinm r9,r1,0,0,18 - tophys(r9,r9) - lwz r11,TI_CPU(r9) - slwi r11,r11,2 - /* Todo make sure all these are in the same page - * and load r22 (@ha part + CPU offset) only once - */ -BEGIN_FTR_SECTION - beq cr1,1f - addis r9,r11,(nap_save_msscr0-KERNELBASE)@ha - lwz r9,nap_save_msscr0@l(r9) - mtspr SPRN_MSSCR0, r9 - sync - isync -1: -END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR) -BEGIN_FTR_SECTION - addis r9,r11,(nap_save_hid1-KERNELBASE)@ha - lwz r9,nap_save_hid1@l(r9) - mtspr SPRN_HID1, r9 -END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX) - b transfer_to_handler_cont - - .data - -_GLOBAL(nap_save_msscr0) - .space 4*NR_CPUS - -_GLOBAL(nap_save_hid1) - .space 4*NR_CPUS - -_GLOBAL(powersave_nap) - .long 0 -_GLOBAL(powersave_lowspeed) - .long 0 - -#ifdef DEBUG -_GLOBAL(nap_enter_count) - .space 4 -_GLOBAL(nap_return_count) - .space 4 -#endif diff --git a/arch/ppc/kernel/idle_power4.S b/arch/ppc/kernel/idle_power4.S deleted file mode 100644 index cc0d535365cd..000000000000 --- a/arch/ppc/kernel/idle_power4.S +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file contains the power_save function for 6xx & 7xxx CPUs - * rewritten in assembler - * - * Warning ! This code assumes that if your machine has a 750fx - * it will have PLL 1 set to low speed mode (used during NAP/DOZE). - * if this is not the case some additional changes will have to - * be done to check a runtime var (a bit like powersave-nap) - * - * 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 - -#undef DEBUG - - .text - -/* - * Init idle, called at early CPU setup time from head.S for each CPU - * So nothing for now. Called with r24 containing CPU number and r3 - * reloc offset - */ - .globl init_idle_power4 -init_idle_power4: - blr - -/* - * Here is the power_save_6xx function. This could eventually be - * split into several functions & changing the function pointer - * depending on the various features. - */ - .globl power4_idle -power4_idle: -BEGIN_FTR_SECTION - blr -END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) - /* We must dynamically check for the NAP feature as it - * can be cleared by CPU init after the fixups are done - */ - lis r4,cur_cpu_spec@ha - lwz r4,cur_cpu_spec@l(r4) - lwz r4,CPU_SPEC_FEATURES(r4) - andi. r0,r4,CPU_FTR_CAN_NAP - beqlr - /* Now check if user or arch enabled NAP mode */ - lis r4,powersave_nap@ha - lwz r4,powersave_nap@l(r4) - cmpwi 0,r4,0 - beqlr - - /* Clear MSR:EE */ - mfmsr r7 - rlwinm r0,r7,0,17,15 - mtmsr r0 - - /* Check current_thread_info()->flags */ - rlwinm r4,r1,0,0,18 - lwz r4,TI_FLAGS(r4) - andi. r0,r4,_TIF_NEED_RESCHED - beq 1f - mtmsr r7 /* out of line this ? */ - blr -1: - /* Go to NAP now */ -BEGIN_FTR_SECTION - DSSALL - sync -END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) - ori r7,r7,MSR_EE /* Could be ommited (already set) */ - oris r7,r7,MSR_POW@h - sync - isync - mtmsr r7 - isync - sync - blr - - .globl powersave_nap -powersave_nap: - .long 0 diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index 5348b820788c..21c8dc90d175 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -158,6 +158,12 @@ struct machdep_calls { /* Idle loop for this platform, leave empty for default idle loop */ void (*idle_loop)(void); + /* + * Function for waiting for work with reduced power in idle loop; + * called with interrupts disabled. + */ + void (*power_save)(void); + /* Function to enable performance monitor counters for this platform, called once per cpu. */ void (*enable_pmcs)(void); @@ -170,9 +176,6 @@ struct machdep_calls { May be NULL. */ void (*init)(void); - void (*idle)(void); - void (*power_save)(void); - void (*heartbeat)(void); unsigned long heartbeat_reset; unsigned long heartbeat_count; @@ -242,8 +245,8 @@ struct machdep_calls { #endif /* CONFIG_KEXEC */ }; -extern void default_idle(void); -extern void native_idle(void); +extern void power4_idle(void); +extern void ppc6xx_idle(void); extern struct machdep_calls ppc_md; extern char cmd_line[COMMAND_LINE_SIZE]; diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index 72bfe3af0460..bd467bf5cf5a 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h @@ -622,6 +622,10 @@ extern void ppc64_runlatch_off(void); extern unsigned long scom970_read(unsigned int address); extern void scom970_write(unsigned int address, unsigned long value); +#else +#define ppc64_runlatch_on() +#define ppc64_runlatch_off() + #endif /* CONFIG_PPC64 */ #define __get_SP() ({unsigned long sp; \ diff --git a/include/asm-ppc/machdep.h b/include/asm-ppc/machdep.h index a3e8a45e45a9..ebbef64e8985 100644 --- a/include/asm-ppc/machdep.h +++ b/include/asm-ppc/machdep.h @@ -44,7 +44,7 @@ struct machdep_calls { void (*power_off)(void); void (*halt)(void); - void (*idle)(void); + void (*idle_loop)(void); void (*power_save)(void); long (*time_init)(void); /* Optional, may be NULL */ -- cgit v1.2.3 From fbd7740fdfdf9475f92287a84085a1913541cd5d Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 27 Mar 2006 15:06:20 +1100 Subject: powerpc: Simplify pSeries idle loop Since pSeries only wants to do something different in the idle loop when there is no work to do, we can simplify the code by implementing ppc_md.power_save functions instead of complete idle loops. There are two versions: one for shared-processor partitions and one for dedicated- processor partitions. With this we also do a cede_processor() call on dedicated processor partitions if the poll_pending() call indicates that the hypervisor has work it wants to do. Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/setup.c | 190 ++++++++++++--------------------- include/asm-powerpc/hvcall.h | 1 + 2 files changed, 71 insertions(+), 120 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 44d5c7fdcd97..213bf983242f 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -81,8 +81,8 @@ extern void find_udbg_vterm(void); int fwnmi_active; /* TRUE if an FWNMI handler is present */ -static void pseries_shared_idle(void); -static void pseries_dedicated_idle(void); +static void pseries_shared_idle_sleep(void); +static void pseries_dedicated_idle_sleep(void); struct mpic *pSeries_mpic; @@ -236,14 +236,13 @@ static void __init pSeries_setup_arch(void) vpa_init(boot_cpuid); if (get_lppaca()->shared_proc) { printk(KERN_INFO "Using shared processor idle loop\n"); - ppc_md.idle_loop = pseries_shared_idle; + ppc_md.power_save = pseries_shared_idle_sleep; } else { printk(KERN_INFO "Using dedicated idle loop\n"); - ppc_md.idle_loop = pseries_dedicated_idle; + ppc_md.power_save = pseries_dedicated_idle_sleep; } } else { printk(KERN_INFO "Using default idle loop\n"); - ppc_md.idle_loop = default_idle; } if (firmware_has_feature(FW_FEATURE_LPAR)) @@ -393,136 +392,87 @@ static int __init pSeries_probe(int platform) DECLARE_PER_CPU(unsigned long, smt_snooze_delay); -static inline void dedicated_idle_sleep(unsigned int cpu) -{ - struct lppaca *plppaca = &lppaca[cpu ^ 1]; - - /* Only sleep if the other thread is not idle */ - if (!(plppaca->idle)) { - local_irq_disable(); - - /* - * We are about to sleep the thread and so wont be polling any - * more. - */ - clear_thread_flag(TIF_POLLING_NRFLAG); - smp_mb__after_clear_bit(); - - /* - * SMT dynamic mode. Cede will result in this thread going - * dormant, if the partner thread is still doing work. Thread - * wakes up if partner goes idle, an interrupt is presented, or - * a prod occurs. Returning from the cede enables external - * interrupts. - */ - if (!need_resched()) - cede_processor(); - else - local_irq_enable(); - set_thread_flag(TIF_POLLING_NRFLAG); - } else { - /* - * Give the HV an opportunity at the processor, since we are - * not doing any work. - */ - poll_pending(); - } -} - -static void pseries_dedicated_idle(void) +static void pseries_dedicated_idle_sleep(void) { unsigned int cpu = smp_processor_id(); unsigned long start_snooze; unsigned long *smt_snooze_delay = &__get_cpu_var(smt_snooze_delay); - set_thread_flag(TIF_POLLING_NRFLAG); - - while (1) { - /* - * Indicate to the HV that we are idle. Now would be - * a good time to find other work to dispatch. - */ - get_lppaca()->idle = 1; - - if (!need_resched()) { - start_snooze = get_tb() + - *smt_snooze_delay * tb_ticks_per_usec; - - while (!need_resched() && !cpu_is_offline(cpu)) { - ppc64_runlatch_off(); - - /* - * Go into low thread priority and possibly - * low power mode. - */ - HMT_low(); - HMT_very_low(); - - if (*smt_snooze_delay != 0 && - get_tb() > start_snooze) { - HMT_medium(); - dedicated_idle_sleep(cpu); - } - - } - - HMT_medium(); - } - get_lppaca()->idle = 0; - ppc64_runlatch_on(); + /* + * Indicate to the HV that we are idle. Now would be + * a good time to find other work to dispatch. + */ + get_lppaca()->idle = 1; - preempt_enable_no_resched(); - schedule(); - preempt_disable(); + /* + * We come in with interrupts disabled, and need_resched() + * has been checked recently. If we should poll for a little + * while, do so. + */ + if (*smt_snooze_delay) { + start_snooze = get_tb() + + *smt_snooze_delay * tb_ticks_per_usec; + local_irq_enable(); + set_thread_flag(TIF_POLLING_NRFLAG); - if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING) - cpu_die(); + while (get_tb() < start_snooze) { + if (need_resched() || cpu_is_offline(cpu)) + goto out; + ppc64_runlatch_off(); + HMT_low(); + HMT_very_low(); + } + + HMT_medium(); + clear_thread_flag(TIF_POLLING_NRFLAG); + smp_mb(); + local_irq_disable(); + if (need_resched() || cpu_is_offline(cpu)) + goto out; } + + /* + * Cede if the other thread is not idle, so that it can + * go single-threaded. If the other thread is idle, + * we ask the hypervisor if it has pending work it + * wants to do and cede if it does. Otherwise we keep + * polling in order to reduce interrupt latency. + * + * Doing the cede when the other thread is active will + * result in this thread going dormant, meaning the other + * thread gets to run in single-threaded (ST) mode, which + * is slightly faster than SMT mode with this thread at + * very low priority. The cede enables interrupts, which + * doesn't matter here. + */ + if (!lppaca[cpu ^ 1].idle || poll_pending() == H_Pending) + cede_processor(); + +out: + HMT_medium(); + get_lppaca()->idle = 0; } -static void pseries_shared_idle(void) +static void pseries_shared_idle_sleep(void) { unsigned int cpu = smp_processor_id(); - while (1) { - /* - * Indicate to the HV that we are idle. Now would be - * a good time to find other work to dispatch. - */ - get_lppaca()->idle = 1; - - while (!need_resched() && !cpu_is_offline(cpu)) { - local_irq_disable(); - ppc64_runlatch_off(); - - /* - * Yield the processor to the hypervisor. We return if - * an external interrupt occurs (which are driven prior - * to returning here) or if a prod occurs from another - * processor. When returning here, external interrupts - * are enabled. - * - * Check need_resched() again with interrupts disabled - * to avoid a race. - */ - if (!need_resched()) - cede_processor(); - else - local_irq_enable(); - - HMT_medium(); - } - - get_lppaca()->idle = 0; - ppc64_runlatch_on(); + /* + * Indicate to the HV that we are idle. Now would be + * a good time to find other work to dispatch. + */ + get_lppaca()->idle = 1; - preempt_enable_no_resched(); - schedule(); - preempt_disable(); + /* + * Yield the processor to the hypervisor. We return if + * an external interrupt occurs (which are driven prior + * to returning here) or if a prod occurs from another + * processor. When returning here, external interrupts + * are enabled. + */ + cede_processor(); - if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING) - cpu_die(); - } + get_lppaca()->idle = 0; } static int pSeries_pci_probe_mode(struct pci_bus *bus) diff --git a/include/asm-powerpc/hvcall.h b/include/asm-powerpc/hvcall.h index 38ca9ad6110d..b72c04f3f551 100644 --- a/include/asm-powerpc/hvcall.h +++ b/include/asm-powerpc/hvcall.h @@ -9,6 +9,7 @@ #define H_Closed 2 /* Resource closed */ #define H_Constrained 4 /* Resource request constrained to max allowed */ #define H_InProgress 14 /* Kind of like busy */ +#define H_Pending 17 /* returned from H_POLL_PENDING */ #define H_Continue 18 /* Returned from H_Join on success */ #define H_LongBusyStartRange 9900 /* Start of long busy range */ #define H_LongBusyOrder1msec 9900 /* Long busy, hint that 1msec is a good time to retry */ -- cgit v1.2.3 From 837c7878771c15ed8d85ecf814ece7fcb4551b46 Mon Sep 17 00:00:00 2001 From: Ben Woodard Date: Wed, 22 Mar 2006 08:09:31 +0100 Subject: [BLOCK] increase size of disk stat counters The kernel's representation of the disk statistics uses the type unsigned which is 32b on both 32b and 64b platforms. Unfortunately, most system tools that work with these numbers that are exported in /proc/diskstats including iostat read these numbers into unsigned longs. This works fine on 32b platforms and when the number of IO transactions are small on 64b platforms. However, when the numbers wrap on 64b platforms & you read the numbers into unsigned longs, and compare the numbers to previous readings, then you get an unsigned representation of a negative number. This looks like a very large 64b number & gives you bizarre readouts in iostat: ilc4: Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util ilc4: sda 5.50 0.00 143.96 0.00 307496983987862656.00 0.00 153748491993931328.00 0.00 2136028725038430.00 7.94 55.12 5.59 80.42 Though fixing iostat in user space is possible, and a quick survey indicates that several other similar tools also use unsigned longs when processing /proc/diskstats. Therefore, it seems like a better approach would be to extend the length of the disk_stats structure on 64b architectures to 64b. The following patch does that. It should not affect the operation on 32b platforms. Signed-off-by: Ben Woodard Cc: Rick Lindsley Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- block/genhd.c | 6 +++--- include/linux/genhd.h | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/block/genhd.c b/block/genhd.c index 64510fd88621..db4c60c802d6 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -454,8 +454,8 @@ static ssize_t disk_stats_read(struct gendisk * disk, char *page) disk_round_stats(disk); preempt_enable(); return sprintf(page, - "%8u %8u %8llu %8u " - "%8u %8u %8llu %8u " + "%8lu %8lu %8llu %8u " + "%8lu %8lu %8llu %8u " "%8u %8u %8u" "\n", disk_stat_read(disk, ios[READ]), @@ -649,7 +649,7 @@ static int diskstats_show(struct seq_file *s, void *v) preempt_disable(); disk_round_stats(gp); preempt_enable(); - seq_printf(s, "%4d %4d %s %u %u %llu %u %u %u %llu %u %u %u %u\n", + seq_printf(s, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", gp->major, n + gp->first_minor, disk_name(gp, n, buf), disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]), (unsigned long long)disk_stat_read(gp, sectors[0]), diff --git a/include/linux/genhd.h b/include/linux/genhd.h index fd647fde5ec1..179fea53fc81 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -89,12 +89,12 @@ struct hd_struct { #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 struct disk_stats { - unsigned sectors[2]; /* READs and WRITEs */ - unsigned ios[2]; - unsigned merges[2]; - unsigned ticks[2]; - unsigned io_ticks; - unsigned time_in_queue; + unsigned long sectors[2]; /* READs and WRITEs */ + unsigned long ios[2]; + unsigned long merges[2]; + unsigned long ticks[2]; + unsigned long io_ticks; + unsigned long time_in_queue; }; struct gendisk { -- cgit v1.2.3 From 9618edab82fda8dbce5ea3abcdac9ded07abb2d4 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 27 Mar 2006 21:48:57 +1100 Subject: powerpc: Fix event-scan code for 32-bit CHRP On CHRP machines we are supposed to call into firmware (RTAS) periodically, to give it a chance to check for errors and other events. Under ppc we had some special code in timer_interrupt to do this, but that didn't get transferred over to arch/powerpc. Instead, we use an array of timer_list structs, one per CPU, and use add_timer_on to make sure each one gets called on the appropriate CPU. With this we can remove the heartbeat_* elements of the ppc_md struct. Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/chrp/chrp.h | 2 +- arch/powerpc/platforms/chrp/setup.c | 64 ++++++++++++++++++++++++++----------- include/asm-powerpc/machdep.h | 4 --- 3 files changed, 46 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/chrp/chrp.h b/arch/powerpc/platforms/chrp/chrp.h index 814f54742e0f..63f0aee4c158 100644 --- a/arch/powerpc/platforms/chrp/chrp.h +++ b/arch/powerpc/platforms/chrp/chrp.h @@ -8,4 +8,4 @@ extern int chrp_set_rtc_time(struct rtc_time *); extern long chrp_time_init(void); extern void chrp_find_bridges(void); -extern void chrp_event_scan(void); +extern void chrp_event_scan(unsigned long); diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index 8bf4307e323d..9c718bb2305e 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,10 @@ EXPORT_SYMBOL(_chrp_type); struct mpic *chrp_mpic; +/* Used for doing CHRP event-scans */ +DEFINE_PER_CPU(struct timer_list, heartbeat_timer); +unsigned long event_scan_interval; + /* * XXX this should be in xmon.h, but putting it there means xmon.h * has to include (to get irqreturn_t), which @@ -229,8 +234,6 @@ void __init chrp_setup_arch(void) { struct device_node *root = find_path_device ("/"); char *machine = NULL; - struct device_node *device; - unsigned int *p = NULL; /* init to some ~sane value until calibrate_delay() runs */ loops_per_jiffy = 50000000/HZ; @@ -287,21 +290,6 @@ void __init chrp_setup_arch(void) */ sio_init(); - /* Get the event scan rate for the rtas so we know how - * often it expects a heartbeat. -- Cort - */ - device = find_devices("rtas"); - if (device) - p = (unsigned int *) get_property - (device, "rtas-event-scan-rate", NULL); - if (p && *p) { - ppc_md.heartbeat = chrp_event_scan; - ppc_md.heartbeat_reset = HZ / (*p * 30) - 1; - ppc_md.heartbeat_count = 1; - printk("RTAS Event Scan Rate: %u (%lu jiffies)\n", - *p, ppc_md.heartbeat_reset); - } - pci_create_OF_bus_map(); /* @@ -312,7 +300,7 @@ void __init chrp_setup_arch(void) } void -chrp_event_scan(void) +chrp_event_scan(unsigned long unused) { unsigned char log[1024]; int ret = 0; @@ -320,7 +308,8 @@ chrp_event_scan(void) /* XXX: we should loop until the hardware says no more error logs -- Cort */ rtas_call(rtas_token("event-scan"), 4, 1, &ret, 0xffffffff, 0, __pa(log), 1024); - ppc_md.heartbeat_count = ppc_md.heartbeat_reset; + mod_timer(&__get_cpu_var(heartbeat_timer), + jiffies + event_scan_interval); } /* @@ -465,6 +454,9 @@ void __init chrp_init_IRQ(void) void __init chrp_init2(void) { + struct device_node *device; + unsigned int *p = NULL; + #ifdef CONFIG_NVRAM chrp_nvram_init(); #endif @@ -476,6 +468,40 @@ chrp_init2(void) request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); + /* Get the event scan rate for the rtas so we know how + * often it expects a heartbeat. -- Cort + */ + device = find_devices("rtas"); + if (device) + p = (unsigned int *) get_property + (device, "rtas-event-scan-rate", NULL); + if (p && *p) { + /* + * Arrange to call chrp_event_scan at least *p times + * per minute. We use 59 rather than 60 here so that + * the rate will be slightly higher than the minimum. + * This all assumes we don't do hotplug CPU on any + * machine that needs the event scans done. + */ + unsigned long interval, offset; + int cpu, ncpus; + struct timer_list *timer; + + interval = HZ * 59 / *p; + offset = HZ; + ncpus = num_online_cpus(); + event_scan_interval = ncpus * interval; + for (cpu = 0; cpu < ncpus; ++cpu) { + timer = &per_cpu(heartbeat_timer, cpu); + setup_timer(timer, chrp_event_scan, 0); + timer->expires = jiffies + offset; + add_timer_on(timer, cpu); + offset += interval; + } + printk("RTAS Event Scan Rate: %u (%lu jiffies)\n", + *p, interval); + } + if (ppc_md.progress) ppc_md.progress(" Have fun! ", 0x7777); } diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index 21c8dc90d175..758e47fe8c1e 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -176,10 +176,6 @@ struct machdep_calls { May be NULL. */ void (*init)(void); - void (*heartbeat)(void); - unsigned long heartbeat_reset; - unsigned long heartbeat_count; - void (*setup_io_mappings)(void); void (*early_serial_map)(void); -- cgit v1.2.3 From bb83da053319e81906473bec08e8f677d6ac615f Mon Sep 17 00:00:00 2001 From: Jeff Dike Date: Mon, 27 Mar 2006 01:14:27 -0800 Subject: [PATCH] uml: fix build warnings in __get_user Fix a gcc warning about losing qualifiers to the first argument of copy_from_user. The typeof change for correctness, and fixes a lot of the warnings, but there are some cases where x has some extra qualifiers, like volatile, which copy_from_user can't know about. For these, the void * cast seems to be necessary. Also cleaned up some of the whitespace and got rid of the emacs comment at the bottom. Signed-off-by: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-um/uaccess.h | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/asm-um/uaccess.h b/include/asm-um/uaccess.h index 2ee028b8de9d..4e460d6f5ac8 100644 --- a/include/asm-um/uaccess.h +++ b/include/asm-um/uaccess.h @@ -41,16 +41,16 @@ #define __get_user(x, ptr) \ ({ \ - const __typeof__(ptr) __private_ptr = ptr; \ - __typeof__(*(__private_ptr)) __private_val; \ - int __private_ret = -EFAULT; \ - (x) = (__typeof__(*(__private_ptr)))0; \ - if (__copy_from_user(&__private_val, (__private_ptr), \ - sizeof(*(__private_ptr))) == 0) {\ - (x) = (__typeof__(*(__private_ptr))) __private_val; \ - __private_ret = 0; \ - } \ - __private_ret; \ + const __typeof__(ptr) __private_ptr = ptr; \ + __typeof__(x) __private_val; \ + int __private_ret = -EFAULT; \ + (x) = (__typeof__(*(__private_ptr)))0; \ + if (__copy_from_user((void *) &__private_val, (__private_ptr), \ + sizeof(*(__private_ptr))) == 0) { \ + (x) = (__typeof__(*(__private_ptr))) __private_val; \ + __private_ret = 0; \ + } \ + __private_ret; \ }) #define get_user(x, ptr) \ @@ -89,14 +89,3 @@ struct exception_table_entry }; #endif - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ -- cgit v1.2.3 From f75ba3ade8a4599d67040a9493d75a864e7b329c Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 27 Mar 2006 01:14:52 -0800 Subject: [PATCH] autofs4: increase module version Update autofs4 version. Signed-off-by: Ian Kent Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/auto_fs4.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/auto_fs4.h b/include/linux/auto_fs4.h index 9343c89d843c..d998ddcf7288 100644 --- a/include/linux/auto_fs4.h +++ b/include/linux/auto_fs4.h @@ -23,7 +23,7 @@ #define AUTOFS_MIN_PROTO_VERSION 3 #define AUTOFS_MAX_PROTO_VERSION 4 -#define AUTOFS_PROTO_SUBVERSION 7 +#define AUTOFS_PROTO_SUBVERSION 10 /* Mask for expire behaviour */ #define AUTOFS_EXP_IMMEDIATE 1 -- cgit v1.2.3 From 5c0a32fc2cd0be912511199449a37a4a6f0f582d Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 27 Mar 2006 01:14:55 -0800 Subject: [PATCH] autofs4: add new packet type for v5 communications This patch define a new autofs packet for autofs v5 and updates the waitq.c functions to handle the additional packet type. Signed-off-by: Ian Kent Cc: Al Viro Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/autofs4/autofs_i.h | 23 +++++++++---- fs/autofs4/waitq.c | 86 +++++++++++++++++++++++++++++++++++++++++------- include/linux/auto_fs4.h | 51 +++++++++++++++++++++++++--- 3 files changed, 136 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index ed388a1d8fc4..37c8d909d1e9 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -77,6 +77,12 @@ struct autofs_wait_queue { int hash; int len; char *name; + u32 dev; + u64 ino; + uid_t uid; + gid_t gid; + pid_t pid; + pid_t tgid; /* This is for status reporting upon return */ int status; atomic_t notified; @@ -180,13 +186,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *, struct autofs_sb_info /* Queue management functions */ -enum autofs_notify -{ - NFY_NONE, - NFY_MOUNT, - NFY_EXPIRE -}; - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); void autofs4_catatonic_mode(struct autofs_sb_info *); @@ -204,6 +203,16 @@ static inline int autofs4_follow_mount(struct vfsmount **mnt, struct dentry **de return res; } +static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) +{ + return new_encode_dev(sbi->sb->s_dev); +} + +static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) +{ + return sbi->sb->s_root->d_inode->i_ino; +} + static inline int simple_positive(struct dentry *dentry) { return dentry->d_inode && !d_unhashed(dentry); diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index b0bb9d43bcd9..12da2c977b0a 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -3,7 +3,7 @@ * linux/fs/autofs/waitq.c * * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 2001-2003 Ian Kent + * Copyright 2001-2006 Ian Kent * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -97,7 +97,10 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, pkt.hdr.proto_version = sbi->version; pkt.hdr.type = type; - if (type == autofs_ptype_missing) { + switch (type) { + /* Kernel protocol v4 missing and expire packets */ + case autofs_ptype_missing: + { struct autofs_packet_missing *mp = &pkt.missing; pktsz = sizeof(*mp); @@ -106,7 +109,10 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, mp->len = wq->len; memcpy(mp->name, wq->name, wq->len); mp->name[wq->len] = '\0'; - } else if (type == autofs_ptype_expire_multi) { + break; + } + case autofs_ptype_expire_multi: + { struct autofs_packet_expire_multi *ep = &pkt.expire_multi; pktsz = sizeof(*ep); @@ -115,7 +121,34 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, ep->len = wq->len; memcpy(ep->name, wq->name, wq->len); ep->name[wq->len] = '\0'; - } else { + break; + } + /* + * Kernel protocol v5 packet for handling indirect and direct + * mount missing and expire requests + */ + case autofs_ptype_missing_indirect: + case autofs_ptype_expire_indirect: + case autofs_ptype_missing_direct: + case autofs_ptype_expire_direct: + { + struct autofs_v5_packet *packet = &pkt.v5_packet; + + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; + packet->len = wq->len; + memcpy(packet->name, wq->name, wq->len); + packet->name[wq->len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; + packet->gid = wq->gid; + packet->pid = wq->pid; + packet->tgid = wq->tgid; + break; + } + default: printk("autofs4_notify_daemon: bad type %d!\n", type); return; } @@ -161,7 +194,9 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, { struct autofs_wait_queue *wq; char *name; - int len, status; + unsigned int len = 0; + unsigned int hash = 0; + int status; /* In catatonic mode, we don't wait for nobody */ if (sbi->catatonic) @@ -171,11 +206,17 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, if (!name) return -ENOMEM; - len = autofs4_getpath(sbi, dentry, &name); - if (!len) { - kfree(name); - return -ENOENT; + /* If this is a direct mount request create a dummy name */ + if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYP_DIRECT)) + len = sprintf(name, "%p", dentry); + else { + len = autofs4_getpath(sbi, dentry, &name); + if (!len) { + kfree(name); + return -ENOENT; + } } + hash = full_name_hash(name, len); if (mutex_lock_interruptible(&sbi->wq_mutex)) { kfree(name); @@ -211,9 +252,15 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, wq->next = sbi->queues; sbi->queues = wq; init_waitqueue_head(&wq->queue); - wq->hash = dentry->d_name.hash; + wq->hash = hash; wq->name = name; wq->len = len; + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; + wq->gid = current->gid; + wq->pid = current->pid; + wq->tgid = current->tgid; wq->status = -EINTR; /* Status return if interrupted */ atomic_set(&wq->wait_ctr, 2); atomic_set(&wq->notified, 1); @@ -227,8 +274,23 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, } if (notify != NFY_NONE && atomic_dec_and_test(&wq->notified)) { - int type = (notify == NFY_MOUNT ? - autofs_ptype_missing : autofs_ptype_expire_multi); + int type; + + if (sbi->version < 5) { + if (notify == NFY_MOUNT) + type = autofs_ptype_missing; + else + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) + type = (sbi->type & AUTOFS_TYP_DIRECT) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else + type = (sbi->type & AUTOFS_TYP_DIRECT) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); diff --git a/include/linux/auto_fs4.h b/include/linux/auto_fs4.h index d998ddcf7288..0a6bc52ffe88 100644 --- a/include/linux/auto_fs4.h +++ b/include/linux/auto_fs4.h @@ -19,18 +19,37 @@ #undef AUTOFS_MIN_PROTO_VERSION #undef AUTOFS_MAX_PROTO_VERSION -#define AUTOFS_PROTO_VERSION 4 +#define AUTOFS_PROTO_VERSION 5 #define AUTOFS_MIN_PROTO_VERSION 3 -#define AUTOFS_MAX_PROTO_VERSION 4 +#define AUTOFS_MAX_PROTO_VERSION 5 -#define AUTOFS_PROTO_SUBVERSION 10 +#define AUTOFS_PROTO_SUBVERSION 0 /* Mask for expire behaviour */ #define AUTOFS_EXP_IMMEDIATE 1 #define AUTOFS_EXP_LEAVES 2 -/* New message type */ -#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ +/* Daemon notification packet types */ +enum autofs_notify { + NFY_NONE, + NFY_MOUNT, + NFY_EXPIRE +}; + +/* Kernel protocol version 4 packet types */ + +/* Expire entry (umount request) */ +#define autofs_ptype_expire_multi 2 + +/* Kernel protocol version 5 packet types */ + +/* Indirect mount missing and expire requests. */ +#define autofs_ptype_missing_indirect 3 +#define autofs_ptype_expire_indirect 4 + +/* Direct mount missing and expire requests */ +#define autofs_ptype_missing_direct 5 +#define autofs_ptype_expire_direct 6 /* v4 multi expire (via pipe) */ struct autofs_packet_expire_multi { @@ -40,14 +59,36 @@ struct autofs_packet_expire_multi { char name[NAME_MAX+1]; }; +/* autofs v5 common packet struct */ +struct autofs_v5_packet { + struct autofs_packet_hdr hdr; + autofs_wqt_t wait_queue_token; + __u32 dev; + __u64 ino; + __u32 uid; + __u32 gid; + __u32 pid; + __u32 tgid; + __u32 len; + char name[NAME_MAX+1]; +}; + +typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; +typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; +typedef struct autofs_v5_packet autofs_packet_missing_direct_t; +typedef struct autofs_v5_packet autofs_packet_expire_direct_t; + union autofs_packet_union { struct autofs_packet_hdr hdr; struct autofs_packet_missing missing; struct autofs_packet_expire expire; struct autofs_packet_expire_multi expire_multi; + struct autofs_v5_packet v5_packet; }; #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) +#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI +#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) #define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) #define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) -- cgit v1.2.3 From efc36aa5608f5717338747e152c23f2cfdb14697 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 27 Mar 2006 01:14:59 -0800 Subject: [PATCH] knfsd: Change the store of auth_domains to not be a 'cache' The 'auth_domain's are simply handles on internal data structures. They do not cache information from user-space, and forcing them into the mold of a 'cache' misrepresents their true nature and causes confusion. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/export.c | 5 +- include/linux/sunrpc/svcauth.h | 12 ++-- net/sunrpc/auth_gss/svcauth_gss.c | 14 ++--- net/sunrpc/sunrpc_syms.c | 4 +- net/sunrpc/svcauth.c | 122 +++++++++++--------------------------- net/sunrpc/svcauth_unix.c | 69 ++++++++++----------- 6 files changed, 81 insertions(+), 145 deletions(-) (limited to 'include') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 417ec02df44f..ac0997731fce 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -242,7 +242,7 @@ static inline int svc_expkey_match (struct svc_expkey *a, struct svc_expkey *b) static inline void svc_expkey_init(struct svc_expkey *new, struct svc_expkey *item) { - cache_get(&item->ek_client->h); + kref_get(&item->ek_client->ref); new->ek_client = item->ek_client; new->ek_fsidtype = item->ek_fsidtype; new->ek_fsid[0] = item->ek_fsid[0]; @@ -474,7 +474,7 @@ static inline int svc_export_match(struct svc_export *a, struct svc_export *b) } static inline void svc_export_init(struct svc_export *new, struct svc_export *item) { - cache_get(&item->ex_client->h); + kref_get(&item->ex_client->ref); new->ex_client = item->ex_client; new->ex_dentry = dget(item->ex_dentry); new->ex_mnt = mntget(item->ex_mnt); @@ -1129,7 +1129,6 @@ exp_delclient(struct nfsctl_client *ncp) */ if (dom) { err = auth_unix_forget_old(dom); - dom->h.expiry_time = get_seconds(); auth_domain_put(dom); } diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index c119ce7cbd22..2fe2087edd66 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -45,9 +45,10 @@ struct svc_rqst; /* forward decl */ * of ip addresses to the given client. */ struct auth_domain { - struct cache_head h; + struct kref ref; + struct hlist_node hash; char *name; - int flavour; + struct auth_ops *flavour; }; /* @@ -86,6 +87,9 @@ struct auth_domain { * * domain_release() * This call releases a domain. + * set_client() + * Givens a pending request (struct svc_rqst), finds and assigns + * an appropriate 'auth_domain' as the client. */ struct auth_ops { char * name; @@ -117,7 +121,7 @@ extern void svc_auth_unregister(rpc_authflavor_t flavor); extern struct auth_domain *unix_domain_find(char *name); extern void auth_domain_put(struct auth_domain *item); extern int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom); -extern struct auth_domain *auth_domain_lookup(struct auth_domain *item, int set); +extern struct auth_domain *auth_domain_lookup(char *name, struct auth_domain *new); extern struct auth_domain *auth_domain_find(char *name); extern struct auth_domain *auth_unix_lookup(struct in_addr addr); extern int auth_unix_forget_old(struct auth_domain *dom); @@ -160,8 +164,6 @@ static inline unsigned long hash_mem(char *buf, int length, int bits) return hash >> (BITS_PER_LONG - bits); } -extern struct cache_detail auth_domain_cache, ip_map_cache; - #endif /* __KERNEL__ */ #endif /* _LINUX_SUNRPC_SVCAUTH_H_ */ diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 23632d84d8d7..6b073c2e6930 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -645,6 +645,8 @@ find_gss_auth_domain(struct gss_ctx *ctx, u32 svc) return auth_domain_find(name); } +static struct auth_ops svcauthops_gss; + int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) { @@ -655,20 +657,18 @@ svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) new = kmalloc(sizeof(*new), GFP_KERNEL); if (!new) goto out; - cache_init(&new->h.h); + kref_init(&new->h.ref); new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL); if (!new->h.name) goto out_free_dom; strcpy(new->h.name, name); - new->h.flavour = RPC_AUTH_GSS; + new->h.flavour = &svcauthops_gss; new->pseudoflavor = pseudoflavor; - new->h.h.expiry_time = NEVER; - test = auth_domain_lookup(&new->h, 1); - if (test == &new->h) { - BUG_ON(atomic_dec_and_test(&new->h.h.refcnt)); - } else { /* XXX Duplicate registration? */ + test = auth_domain_lookup(name, &new->h); + if (test != &new->h) { /* XXX Duplicate registration? */ auth_domain_put(&new->h); + /* dangling ref-count... */ goto out; } return 0; diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 9f7373203592..40401196e7de 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -142,6 +142,7 @@ EXPORT_SYMBOL(nlm_debug); extern int register_rpc_pipefs(void); extern void unregister_rpc_pipefs(void); +extern struct cache_detail ip_map_cache; static int __init init_sunrpc(void) @@ -158,7 +159,6 @@ init_sunrpc(void) #ifdef CONFIG_PROC_FS rpc_proc_init(); #endif - cache_register(&auth_domain_cache); cache_register(&ip_map_cache); out: return err; @@ -169,8 +169,6 @@ cleanup_sunrpc(void) { unregister_rpc_pipefs(); rpc_destroy_mempool(); - if (cache_unregister(&auth_domain_cache)) - printk(KERN_ERR "sunrpc: failed to unregister auth_domain cache\n"); if (cache_unregister(&ip_map_cache)) printk(KERN_ERR "sunrpc: failed to unregister ip_map cache\n"); #ifdef RPC_DEBUG diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c index dda4f0c63511..5b28c6176806 100644 --- a/net/sunrpc/svcauth.c +++ b/net/sunrpc/svcauth.c @@ -106,112 +106,56 @@ svc_auth_unregister(rpc_authflavor_t flavor) EXPORT_SYMBOL(svc_auth_unregister); /************************************************** - * cache for domain name to auth_domain - * Entries are only added by flavours which will normally - * have a structure that 'inherits' from auth_domain. - * e.g. when an IP -> domainname is given to auth_unix, - * and the domain name doesn't exist, it will create a - * auth_unix_domain and add it to this hash table. - * If it finds the name does exist, but isn't AUTH_UNIX, - * it will complain. + * 'auth_domains' are stored in a hash table indexed by name. + * When the last reference to an 'auth_domain' is dropped, + * the object is unhashed and freed. + * If auth_domain_lookup fails to find an entry, it will return + * it's second argument 'new'. If this is non-null, it will + * have been atomically linked into the table. */ -/* - * Auth auth_domain cache is somewhat different to other caches, - * largely because the entries are possibly of different types: - * each auth flavour has it's own type. - * One consequence of this that DefineCacheLookup cannot - * allocate a new structure as it cannot know the size. - * Notice that the "INIT" code fragment is quite different - * from other caches. When auth_domain_lookup might be - * creating a new domain, the new domain is passed in - * complete and it is used as-is rather than being copied into - * another structure. - */ #define DN_HASHBITS 6 #define DN_HASHMAX (1<flavour]->domain_release(dom); -} - - -struct cache_detail auth_domain_cache = { - .owner = THIS_MODULE, - .hash_size = DN_HASHMAX, - .hash_table = auth_domain_table, - .name = "auth.domain", - .cache_put = auth_domain_drop, -}; +static struct hlist_head auth_domain_table[DN_HASHMAX]; +static spinlock_t auth_domain_lock = SPIN_LOCK_UNLOCKED; void auth_domain_put(struct auth_domain *dom) { - auth_domain_drop(&dom->h, &auth_domain_cache); -} - -static inline int auth_domain_hash(struct auth_domain *item) -{ - return hash_str(item->name, DN_HASHBITS); -} -static inline int auth_domain_match(struct auth_domain *tmp, struct auth_domain *item) -{ - return strcmp(tmp->name, item->name) == 0; + if (atomic_dec_and_lock(&dom->ref.refcount, &auth_domain_lock)) { + hlist_del(&dom->hash); + dom->flavour->domain_release(dom); + } } struct auth_domain * -auth_domain_lookup(struct auth_domain *item, int set) +auth_domain_lookup(char *name, struct auth_domain *new) { - struct auth_domain *tmp = NULL; - struct cache_head **hp, **head; - head = &auth_domain_cache.hash_table[auth_domain_hash(item)]; - - if (set) - write_lock(&auth_domain_cache.hash_lock); - else - read_lock(&auth_domain_cache.hash_lock); - for (hp=head; *hp != NULL; hp = &tmp->h.next) { - tmp = container_of(*hp, struct auth_domain, h); - if (!auth_domain_match(tmp, item)) - continue; - if (!set) { - cache_get(&tmp->h); - goto out_noset; + struct auth_domain *hp; + struct hlist_head *head; + struct hlist_node *np; + + head = &auth_domain_table[hash_str(name, DN_HASHBITS)]; + + spin_lock(&auth_domain_lock); + + hlist_for_each_entry(hp, np, head, hash) { + if (strcmp(hp->name, name)==0) { + kref_get(&hp->ref); + spin_unlock(&auth_domain_lock); + return hp; } - *hp = tmp->h.next; - tmp->h.next = NULL; - auth_domain_drop(&tmp->h, &auth_domain_cache); - goto out_set; } - /* Didn't find anything */ - if (!set) - goto out_nada; - auth_domain_cache.entries++; -out_set: - item->h.next = *head; - *head = &item->h; - cache_get(&item->h); - write_unlock(&auth_domain_cache.hash_lock); - cache_fresh(&auth_domain_cache, &item->h, item->h.expiry_time); - cache_get(&item->h); - return item; -out_nada: - tmp = NULL; -out_noset: - read_unlock(&auth_domain_cache.hash_lock); - return tmp; + if (new) { + hlist_add_head(&new->hash, head); + kref_get(&new->ref); + } + spin_unlock(&auth_domain_lock); + return new; } struct auth_domain *auth_domain_find(char *name) { - struct auth_domain *rv, ad; - - ad.name = name; - rv = auth_domain_lookup(&ad, 0); - return rv; + return auth_domain_lookup(name, NULL); } diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 3e6c694bbad1..17e8b2a3130c 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -27,41 +27,35 @@ struct unix_domain { /* other stuff later */ }; +extern struct auth_ops svcauth_unix; + struct auth_domain *unix_domain_find(char *name) { - struct auth_domain *rv, ud; - struct unix_domain *new; - - ud.name = name; - - rv = auth_domain_lookup(&ud, 0); - - foundit: - if (rv && rv->flavour != RPC_AUTH_UNIX) { - auth_domain_put(rv); - return NULL; - } - if (rv) - return rv; - - new = kmalloc(sizeof(*new), GFP_KERNEL); - if (new == NULL) - return NULL; - cache_init(&new->h.h); - new->h.name = kstrdup(name, GFP_KERNEL); - new->h.flavour = RPC_AUTH_UNIX; - new->addr_changes = 0; - new->h.h.expiry_time = NEVER; - - rv = auth_domain_lookup(&new->h, 2); - if (rv == &new->h) { - if (atomic_dec_and_test(&new->h.h.refcnt)) BUG(); - } else { - auth_domain_put(&new->h); - goto foundit; + struct auth_domain *rv; + struct unix_domain *new = NULL; + + rv = auth_domain_lookup(name, NULL); + while(1) { + if (rv != &new->h) { + if (new) auth_domain_put(&new->h); + return rv; + } + if (rv && rv->flavour != &svcauth_unix) { + auth_domain_put(rv); + return NULL; + } + if (rv) + return rv; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (new == NULL) + return NULL; + kref_init(&new->h.ref); + new->h.name = kstrdup(name, GFP_KERNEL); + new->h.flavour = &svcauth_unix; + new->addr_changes = 0; + rv = auth_domain_lookup(name, &new->h); } - - return rv; } static void svcauth_unix_domain_release(struct auth_domain *dom) @@ -130,7 +124,7 @@ static inline void ip_map_init(struct ip_map *new, struct ip_map *item) } static inline void ip_map_update(struct ip_map *new, struct ip_map *item) { - cache_get(&item->m_client->h.h); + kref_get(&item->m_client->h.ref); new->m_client = item->m_client; new->m_add_change = item->m_add_change; } @@ -272,7 +266,7 @@ int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom) struct unix_domain *udom; struct ip_map ip, *ipmp; - if (dom->flavour != RPC_AUTH_UNIX) + if (dom->flavour != &svcauth_unix) return -EINVAL; udom = container_of(dom, struct unix_domain, h); strcpy(ip.m_class, "nfsd"); @@ -295,7 +289,7 @@ int auth_unix_forget_old(struct auth_domain *dom) { struct unix_domain *udom; - if (dom->flavour != RPC_AUTH_UNIX) + if (dom->flavour != &svcauth_unix) return -EINVAL; udom = container_of(dom, struct unix_domain, h); udom->addr_changes++; @@ -323,7 +317,7 @@ struct auth_domain *auth_unix_lookup(struct in_addr addr) rv = NULL; } else { rv = &ipm->m_client->h; - cache_get(&rv->h); + kref_get(&rv->ref); } ip_map_put(&ipm->h, &ip_map_cache); return rv; @@ -332,7 +326,6 @@ struct auth_domain *auth_unix_lookup(struct in_addr addr) void svcauth_unix_purge(void) { cache_purge(&ip_map_cache); - cache_purge(&auth_domain_cache); } static int @@ -361,7 +354,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) return SVC_DENIED; case 0: rqstp->rq_client = &ipm->m_client->h; - cache_get(&rqstp->rq_client->h); + kref_get(&rqstp->rq_client->ref); ip_map_put(&ipm->h, &ip_map_cache); break; } -- cgit v1.2.3 From eab7e2e647c348b418e8715ecaca0177e1b473c7 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 27 Mar 2006 01:15:00 -0800 Subject: [PATCH] knfsd: Break the hard linkage from svc_expkey to svc_export Current svc_expkey holds a pointer to the svc_export structure, so updates to that structure have to be in-place, which is a wart on the whole cache infrastruct. So we break that linkage and just do a second lookup. If this became a performance issue, it would be possible to put a direct link back in which was only used conditionally. i.e. when an object is replaced in the cache, we set a flag in the old object. When dereferencing the link from svc_expkey, if the flag is set, we drop the reference and do a fresh lookup. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/export.c | 60 ++++++++++++++++++++++++++++++--------------- include/linux/nfsd/export.h | 20 +++------------ 2 files changed, 44 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index ac0997731fce..587829ed651c 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -73,8 +73,10 @@ void expkey_put(struct cache_head *item, struct cache_detail *cd) if (cache_put(item, cd)) { struct svc_expkey *key = container_of(item, struct svc_expkey, h); if (test_bit(CACHE_VALID, &item->flags) && - !test_bit(CACHE_NEGATIVE, &item->flags)) - exp_put(key->ek_export); + !test_bit(CACHE_NEGATIVE, &item->flags)) { + dput(key->ek_dentry); + mntput(key->ek_mnt); + } auth_domain_put(key->ek_client); kfree(key); } @@ -164,26 +166,18 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) } else { struct nameidata nd; struct svc_expkey *ek; - struct svc_export *exp; err = path_lookup(buf, 0, &nd); if (err) goto out; dprintk("Found the path %s\n", buf); - exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); - - err = -ENOENT; - if (!exp) - goto out_nd; - key.ek_export = exp; - dprintk("And found export\n"); + key.ek_mnt = nd.mnt; + key.ek_dentry = nd.dentry; ek = svc_expkey_lookup(&key, 1); if (ek) expkey_put(&ek->h, &svc_expkey_cache); - exp_put(exp); err = 0; - out_nd: path_release(&nd); } cache_flush(); @@ -214,7 +208,7 @@ static int expkey_show(struct seq_file *m, if (test_bit(CACHE_VALID, &h->flags) && !test_bit(CACHE_NEGATIVE, &h->flags)) { seq_printf(m, " "); - seq_path(m, ek->ek_export->ex_mnt, ek->ek_export->ex_dentry, "\\ \t\n"); + seq_path(m, ek->ek_mnt, ek->ek_dentry, "\\ \t\n"); } seq_printf(m, "\n"); return 0; @@ -252,8 +246,8 @@ static inline void svc_expkey_init(struct svc_expkey *new, struct svc_expkey *it static inline void svc_expkey_update(struct svc_expkey *new, struct svc_expkey *item) { - cache_get(&item->ek_export->h); - new->ek_export = item->ek_export; + new->ek_mnt = mntget(item->ek_mnt); + new->ek_dentry = dget(item->ek_dentry); } static DefineSimpleCacheLookup(svc_expkey,0) /* no inplace updates */ @@ -519,7 +513,8 @@ static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv, key.ek_client = clp; key.ek_fsidtype = fsid_type; memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); - key.ek_export = exp; + key.ek_mnt = exp->ex_mnt; + key.ek_dentry = exp->ex_dentry; key.h.expiry_time = NEVER; key.h.flags = 0; @@ -741,8 +736,8 @@ exp_export(struct nfsctl_export *nxp) if ((nxp->ex_flags & NFSEXP_FSID) && (fsid_key = exp_get_fsid_key(clp, nxp->ex_dev)) && !IS_ERR(fsid_key) && - fsid_key->ek_export && - fsid_key->ek_export != exp) + fsid_key->ek_mnt && + (fsid_key->ek_mnt != nd.mnt || fsid_key->ek_dentry != nd.dentry) ) goto finish; if (exp) { @@ -912,6 +907,24 @@ out: return err; } +struct svc_export * +exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, + struct cache_req *reqp) +{ + struct svc_export *exp; + struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); + if (!ek || IS_ERR(ek)) + return ERR_PTR(PTR_ERR(ek)); + + exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp); + expkey_put(&ek->h, &svc_expkey_cache); + + if (!exp || IS_ERR(exp)) + return ERR_PTR(PTR_ERR(exp)); + return exp; +} + + /* * Called when we need the filehandle for the root of the pseudofs, * for a given NFSv4 client. The root is defined to be the @@ -922,6 +935,7 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, struct cache_req *creq) { struct svc_expkey *fsid_key; + struct svc_export *exp; int rv; u32 fsidv[2]; @@ -933,8 +947,14 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, if (!fsid_key || IS_ERR(fsid_key)) return nfserr_perm; - rv = fh_compose(fhp, fsid_key->ek_export, - fsid_key->ek_export->ex_dentry, NULL); + exp = exp_get_by_name(clp, fsid_key->ek_mnt, fsid_key->ek_dentry, creq); + if (exp == NULL) + rv = nfserr_perm; + else if (IS_ERR(exp)) + rv = nfserrno(PTR_ERR(exp)); + else + rv = fh_compose(fhp, exp, + fsid_key->ek_dentry, NULL); expkey_put(&fsid_key->h, &svc_expkey_cache); return rv; } diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 6bad4766d3d9..d52e0b7ad37b 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -67,7 +67,8 @@ struct svc_expkey { int ek_fsidtype; u32 ek_fsid[3]; - struct svc_export * ek_export; + struct vfsmount * ek_mnt; + struct dentry * ek_dentry; }; #define EX_SECURE(exp) (!((exp)->ex_flags & NFSEXP_INSECURE_PORT)) @@ -114,22 +115,9 @@ static inline void exp_get(struct svc_export *exp) { cache_get(&exp->h); } -static inline struct svc_export * +extern struct svc_export * exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, - struct cache_req *reqp) -{ - struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); - if (ek && !IS_ERR(ek)) { - struct svc_export *exp = ek->ek_export; - int err; - exp_get(exp); - expkey_put(&ek->h, &svc_expkey_cache); - if ((err = cache_check(&svc_export_cache, &exp->h, reqp))) - exp = ERR_PTR(err); - return exp; - } else - return ERR_PTR(PTR_ERR(ek)); -} + struct cache_req *reqp); #endif /* __KERNEL__ */ -- cgit v1.2.3 From 7d317f2c9f1e9dcf4f632fa98f91d1d4a36c4cae Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 27 Mar 2006 01:15:01 -0800 Subject: [PATCH] knfsd: Get rid of 'inplace' sunrpc caches These were an unnecessary wart. Also only have one 'DefineSimpleCache..' instead of two. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/export.c | 4 ++-- fs/nfsd/nfs4idmap.c | 10 ++-------- include/linux/sunrpc/cache.h | 28 +++++++++++----------------- net/sunrpc/auth_gss/svcauth_gss.c | 4 ++-- net/sunrpc/svcauth_unix.c | 2 +- 5 files changed, 18 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 587829ed651c..c591761a1ad6 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -250,7 +250,7 @@ static inline void svc_expkey_update(struct svc_expkey *new, struct svc_expkey * new->ek_dentry = dget(item->ek_dentry); } -static DefineSimpleCacheLookup(svc_expkey,0) /* no inplace updates */ +static DefineSimpleCacheLookup(svc_expkey, svc_expkey) #define EXPORT_HASHBITS 8 #define EXPORT_HASHMAX (1<< EXPORT_HASHBITS) @@ -482,7 +482,7 @@ static inline void svc_export_update(struct svc_export *new, struct svc_export * new->ex_fsid = item->ex_fsid; } -static DefineSimpleCacheLookup(svc_export,1) /* allow inplace updates */ +static DefineSimpleCacheLookup(svc_export, svc_export) struct svc_expkey * diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 13369650cdf9..dea690aa8bb5 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -76,12 +76,6 @@ struct ent { char authname[IDMAP_NAMESZ]; }; -#define DefineSimpleCacheLookupMap(STRUCT, FUNC) \ - DefineCacheLookup(struct STRUCT, h, FUNC##_lookup, \ - (struct STRUCT *item, int set), /*no setup */, \ - & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \ - STRUCT##_init(new, item), STRUCT##_update(tmp, item), 0) - /* Common entry handling */ #define ENT_HASHBITS 8 @@ -264,7 +258,7 @@ out: return error; } -static DefineSimpleCacheLookupMap(ent, idtoname); +static DefineSimpleCacheLookup(ent, idtoname); /* * Name -> ID cache @@ -390,7 +384,7 @@ out: return (error); } -static DefineSimpleCacheLookupMap(ent, nametoid); +static DefineSimpleCacheLookup(ent, nametoid); /* * Exported API diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index c4e3ea7cf154..405ac14e509a 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -133,14 +133,11 @@ struct cache_deferred_req { * If "set" == 0 : * If an entry is found, it is returned * If no entry is found, a new non-VALID entry is created. - * If "set" == 1 and INPLACE == 0 : + * If "set" == 1 : * If no entry is found a new one is inserted with data from "template" * If a non-CACHE_VALID entry is found, it is updated from template using UPDATE * If a CACHE_VALID entry is found, a new entry is swapped in with data * from "template" - * If set == 1, and INPLACE == 1 : - * As above, except that if a CACHE_VALID entry is found, we UPDATE in place - * instead of swapping in a new entry. * * If the passed handle has the CACHE_NEGATIVE flag set, then UPDATE is not * run but insteead CACHE_NEGATIVE is set in any new item. @@ -159,13 +156,8 @@ struct cache_deferred_req { * TEST tests if "tmp" matches "item" * INIT copies key information from "item" to "new" * UPDATE copies content information from "item" to "tmp" - * INPLACE is true if updates can happen inplace rather than allocating a new structure - * - * WARNING: any substantial changes to this must be reflected in - * net/sunrpc/svcauth.c(auth_domain_lookup) - * which is a similar routine that is open-coded. */ -#define DefineCacheLookup(RTN,MEMBER,FNAME,ARGS,SETUP,DETAIL,HASHFN,TEST,INIT,UPDATE,INPLACE) \ +#define DefineCacheLookup(RTN,MEMBER,FNAME,ARGS,SETUP,DETAIL,HASHFN,TEST,INIT,UPDATE) \ RTN *FNAME ARGS \ { \ RTN *tmp, *new=NULL; \ @@ -179,13 +171,13 @@ RTN *FNAME ARGS \ tmp = container_of(*hp, RTN, MEMBER); \ if (TEST) { /* found a match */ \ \ - if (set && !INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags) && !new) \ + if (set && test_bit(CACHE_VALID, &tmp->MEMBER.flags) && !new) \ break; \ \ if (new) \ {INIT;} \ if (set) { \ - if (!INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags))\ + if (test_bit(CACHE_VALID, &tmp->MEMBER.flags))\ { /* need to swap in new */ \ RTN *t2; \ \ @@ -206,7 +198,7 @@ RTN *FNAME ARGS \ else read_unlock(&(DETAIL)->hash_lock); \ if (set) \ cache_fresh(DETAIL, &tmp->MEMBER, item->MEMBER.expiry_time); \ - if (set && !INPLACE && new) cache_fresh(DETAIL, &new->MEMBER, 0); \ + if (set && new) cache_fresh(DETAIL, &new->MEMBER, 0); \ if (new) (DETAIL)->cache_put(&new->MEMBER, DETAIL); \ return tmp; \ } \ @@ -239,10 +231,12 @@ RTN *FNAME ARGS \ return NULL; \ } -#define DefineSimpleCacheLookup(STRUCT,INPLACE) \ - DefineCacheLookup(struct STRUCT, h, STRUCT##_lookup, (struct STRUCT *item, int set), /*no setup */, \ - & STRUCT##_cache, STRUCT##_hash(item), STRUCT##_match(item, tmp),\ - STRUCT##_init(new, item), STRUCT##_update(tmp, item),INPLACE) +#define DefineSimpleCacheLookup(STRUCT, FUNC) \ + DefineCacheLookup(struct STRUCT, h, FUNC##_lookup, \ + (struct STRUCT *item, int set), /*no setup */, \ + & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \ + STRUCT##_init(new, item), STRUCT##_update(tmp, item)) + #define cache_for_each(pos, detail, index, member) \ for (({read_lock(&(detail)->hash_lock); index = (detail)->hash_size;}) ; \ diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 6b073c2e6930..aadb4e8d6aa7 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -259,7 +259,7 @@ static struct cache_detail rsi_cache = { .cache_parse = rsi_parse, }; -static DefineSimpleCacheLookup(rsi, 0) +static DefineSimpleCacheLookup(rsi, rsi) /* * The rpcsec_context cache is used to store a context that is @@ -446,7 +446,7 @@ static struct cache_detail rsc_cache = { .cache_parse = rsc_parse, }; -static DefineSimpleCacheLookup(rsc, 0); +static DefineSimpleCacheLookup(rsc, rsc); static struct rsc * gss_svc_searchbyctx(struct xdr_netobj *handle) diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 17e8b2a3130c..7ddf068b5b25 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -258,7 +258,7 @@ struct cache_detail ip_map_cache = { .cache_show = ip_map_show, }; -static DefineSimpleCacheLookup(ip_map, 0) +static DefineSimpleCacheLookup(ip_map, ip_map) int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom) -- cgit v1.2.3 From 15a5f6bd23eddd5b3be80366f364be04fb1c1c99 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 27 Mar 2006 01:15:02 -0800 Subject: [PATCH] knfsd: Create cache_lookup function instead of using a macro to declare one The C++-like 'template' approach proves to be too ugly and hard to work with. The old 'template' won't go away until all users are updated. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sunrpc/cache.h | 12 ++++++ net/sunrpc/cache.c | 98 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) (limited to 'include') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 405ac14e509a..3e17a5ff1dea 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -81,6 +81,11 @@ struct cache_detail { struct cache_detail *cd, struct cache_head *h); + struct cache_head * (*alloc)(void); + int (*match)(struct cache_head *orig, struct cache_head *new); + void (*init)(struct cache_head *orig, struct cache_head *new); + void (*update)(struct cache_head *orig, struct cache_head *new); + /* fields below this comment are for internal use * and should not be touched by cache owners */ @@ -237,6 +242,13 @@ RTN *FNAME ARGS \ & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \ STRUCT##_init(new, item), STRUCT##_update(tmp, item)) +extern struct cache_head * +sunrpc_cache_lookup(struct cache_detail *detail, + struct cache_head *key, int hash); +extern struct cache_head * +sunrpc_cache_update(struct cache_detail *detail, + struct cache_head *new, struct cache_head *old, int hash); + #define cache_for_each(pos, detail, index, member) \ for (({read_lock(&(detail)->hash_lock); index = (detail)->hash_size;}) ; \ diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 0acccfeeb284..4449dc52edf5 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -47,6 +47,104 @@ void cache_init(struct cache_head *h) h->last_refresh = now; } +struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, + struct cache_head *key, int hash) +{ + struct cache_head **head, **hp; + struct cache_head *new = NULL; + + head = &detail->hash_table[hash]; + + read_lock(&detail->hash_lock); + + for (hp=head; *hp != NULL ; hp = &(*hp)->next) { + struct cache_head *tmp = *hp; + if (detail->match(tmp, key)) { + cache_get(tmp); + read_unlock(&detail->hash_lock); + return tmp; + } + } + read_unlock(&detail->hash_lock); + /* Didn't find anything, insert an empty entry */ + + new = detail->alloc(); + if (!new) + return NULL; + cache_init(new); + + write_lock(&detail->hash_lock); + + /* check if entry appeared while we slept */ + for (hp=head; *hp != NULL ; hp = &(*hp)->next) { + struct cache_head *tmp = *hp; + if (detail->match(tmp, key)) { + cache_get(tmp); + write_unlock(&detail->hash_lock); + detail->cache_put(new, detail); + return tmp; + } + } + detail->init(new, key); + new->next = *head; + *head = new; + detail->entries++; + cache_get(new); + write_unlock(&detail->hash_lock); + + return new; +} +EXPORT_SYMBOL(sunrpc_cache_lookup); + +struct cache_head *sunrpc_cache_update(struct cache_detail *detail, + struct cache_head *new, struct cache_head *old, int hash) +{ + /* The 'old' entry is to be replaced by 'new'. + * If 'old' is not VALID, we update it directly, + * otherwise we need to replace it + */ + struct cache_head **head; + struct cache_head *tmp; + + if (!test_bit(CACHE_VALID, &old->flags)) { + write_lock(&detail->hash_lock); + if (!test_bit(CACHE_VALID, &old->flags)) { + if (test_bit(CACHE_NEGATIVE, &new->flags)) + set_bit(CACHE_NEGATIVE, &old->flags); + else + detail->update(old, new); + /* FIXME cache_fresh should come first */ + write_unlock(&detail->hash_lock); + cache_fresh(detail, old, new->expiry_time); + return old; + } + write_unlock(&detail->hash_lock); + } + /* We need to insert a new entry */ + tmp = detail->alloc(); + if (!tmp) { + detail->cache_put(old, detail); + return NULL; + } + cache_init(tmp); + detail->init(tmp, old); + head = &detail->hash_table[hash]; + + write_lock(&detail->hash_lock); + if (test_bit(CACHE_NEGATIVE, &new->flags)) + set_bit(CACHE_NEGATIVE, &tmp->flags); + else + detail->update(tmp, new); + tmp->next = *head; + *head = tmp; + cache_get(tmp); + write_unlock(&detail->hash_lock); + cache_fresh(detail, tmp, new->expiry_time); + cache_fresh(detail, old, 0); + detail->cache_put(old, detail); + return tmp; +} +EXPORT_SYMBOL(sunrpc_cache_update); static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h); /* -- cgit v1.2.3 From 4d90452cb23b08a9a9dd001010f0ee6b1ee83a45 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 27 Mar 2006 01:15:07 -0800 Subject: [PATCH] knfsd: Remove DefineCacheLookup This has been replaced by more traditional code. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sunrpc/cache.h | 113 ------------------------------------------- 1 file changed, 113 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 3e17a5ff1dea..afc481dd02dd 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -128,119 +128,6 @@ struct cache_deferred_req { int too_many); }; -/* - * just like a template in C++, this macro does cache lookup - * for us. - * The function is passed some sort of HANDLE from which a cache_detail - * structure can be determined (via SETUP, DETAIL), a template - * cache entry (type RTN*), and a "set" flag. Using the HASHFN and the - * TEST, the function will try to find a matching cache entry in the cache. - * If "set" == 0 : - * If an entry is found, it is returned - * If no entry is found, a new non-VALID entry is created. - * If "set" == 1 : - * If no entry is found a new one is inserted with data from "template" - * If a non-CACHE_VALID entry is found, it is updated from template using UPDATE - * If a CACHE_VALID entry is found, a new entry is swapped in with data - * from "template" - * - * If the passed handle has the CACHE_NEGATIVE flag set, then UPDATE is not - * run but insteead CACHE_NEGATIVE is set in any new item. - - * In any case, the new entry is returned with a reference count. - * - * - * RTN is a struct type for a cache entry - * MEMBER is the member of the cache which is cache_head, which must be first - * FNAME is the name for the function - * ARGS are arguments to function and must contain RTN *item, int set. May - * also contain something to be usedby SETUP or DETAIL to find cache_detail. - * SETUP locates the cache detail and makes it available as... - * DETAIL identifies the cache detail, possibly set up by SETUP - * HASHFN returns a hash value of the cache entry "item" - * TEST tests if "tmp" matches "item" - * INIT copies key information from "item" to "new" - * UPDATE copies content information from "item" to "tmp" - */ -#define DefineCacheLookup(RTN,MEMBER,FNAME,ARGS,SETUP,DETAIL,HASHFN,TEST,INIT,UPDATE) \ -RTN *FNAME ARGS \ -{ \ - RTN *tmp, *new=NULL; \ - struct cache_head **hp, **head; \ - SETUP; \ - head = &(DETAIL)->hash_table[HASHFN]; \ - retry: \ - if (set||new) write_lock(&(DETAIL)->hash_lock); \ - else read_lock(&(DETAIL)->hash_lock); \ - for(hp=head; *hp != NULL; hp = &tmp->MEMBER.next) { \ - tmp = container_of(*hp, RTN, MEMBER); \ - if (TEST) { /* found a match */ \ - \ - if (set && test_bit(CACHE_VALID, &tmp->MEMBER.flags) && !new) \ - break; \ - \ - if (new) \ - {INIT;} \ - if (set) { \ - if (test_bit(CACHE_VALID, &tmp->MEMBER.flags))\ - { /* need to swap in new */ \ - RTN *t2; \ - \ - new->MEMBER.next = tmp->MEMBER.next; \ - *hp = &new->MEMBER; \ - tmp->MEMBER.next = NULL; \ - t2 = tmp; tmp = new; new = t2; \ - } \ - if (test_bit(CACHE_NEGATIVE, &item->MEMBER.flags)) \ - set_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags); \ - else { \ - UPDATE; \ - clear_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags); \ - } \ - } \ - cache_get(&tmp->MEMBER); \ - if (set||new) write_unlock(&(DETAIL)->hash_lock); \ - else read_unlock(&(DETAIL)->hash_lock); \ - if (set) \ - cache_fresh(DETAIL, &tmp->MEMBER, item->MEMBER.expiry_time); \ - if (set && new) cache_fresh(DETAIL, &new->MEMBER, 0); \ - if (new) (DETAIL)->cache_put(&new->MEMBER, DETAIL); \ - return tmp; \ - } \ - } \ - /* Didn't find anything */ \ - if (new) { \ - INIT; \ - new->MEMBER.next = *head; \ - *head = &new->MEMBER; \ - (DETAIL)->entries ++; \ - cache_get(&new->MEMBER); \ - if (set) { \ - tmp = new; \ - if (test_bit(CACHE_NEGATIVE, &item->MEMBER.flags)) \ - set_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags); \ - else {UPDATE;} \ - } \ - } \ - if (set||new) write_unlock(&(DETAIL)->hash_lock); \ - else read_unlock(&(DETAIL)->hash_lock); \ - if (new && set) \ - cache_fresh(DETAIL, &new->MEMBER, item->MEMBER.expiry_time); \ - if (new) \ - return new; \ - new = kmalloc(sizeof(*new), GFP_KERNEL); \ - if (new) { \ - cache_init(&new->MEMBER); \ - goto retry; \ - } \ - return NULL; \ -} - -#define DefineSimpleCacheLookup(STRUCT, FUNC) \ - DefineCacheLookup(struct STRUCT, h, FUNC##_lookup, \ - (struct STRUCT *item, int set), /*no setup */, \ - & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \ - STRUCT##_init(new, item), STRUCT##_update(tmp, item)) extern struct cache_head * sunrpc_cache_lookup(struct cache_detail *detail, -- cgit v1.2.3 From ebd0cb1af3be2729cc1f574681dfba01fcf458d9 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 27 Mar 2006 01:15:08 -0800 Subject: [PATCH] knfsd: Unexport cache_fresh and fix a small race Cache_fresh is now only used in cache.c, so unexport it. Part of cache_fresh (setting CACHE_VALID) should really be done under the lock, while part (calling cache_revisit_request etc) must be done outside the lock. So we split it up appropriately. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sunrpc/cache.h | 2 -- net/sunrpc/cache.c | 51 ++++++++++++++++++++++++++------------------ net/sunrpc/sunrpc_syms.c | 1 - 3 files changed, 30 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index afc481dd02dd..a37fead1873b 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -165,8 +165,6 @@ static inline int cache_put(struct cache_head *h, struct cache_detail *cd) } extern void cache_init(struct cache_head *h); -extern void cache_fresh(struct cache_detail *detail, - struct cache_head *head, time_t expiry); extern int cache_check(struct cache_detail *detail, struct cache_head *h, struct cache_req *rqstp); extern void cache_flush(void); diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index b242f491cea9..edcda4fd88e8 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -96,6 +96,27 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, } EXPORT_SYMBOL(sunrpc_cache_lookup); + +static void queue_loose(struct cache_detail *detail, struct cache_head *ch); + +static int cache_fresh_locked(struct cache_head *head, time_t expiry) +{ + head->expiry_time = expiry; + head->last_refresh = get_seconds(); + return !test_and_set_bit(CACHE_VALID, &head->flags); +} + +static void cache_fresh_unlocked(struct cache_head *head, + struct cache_detail *detail, int new) +{ + if (new) + cache_revisit_request(head); + if (test_and_clear_bit(CACHE_PENDING, &head->flags)) { + cache_revisit_request(head); + queue_loose(detail, head); + } +} + struct cache_head *sunrpc_cache_update(struct cache_detail *detail, struct cache_head *new, struct cache_head *old, int hash) { @@ -105,6 +126,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail, */ struct cache_head **head; struct cache_head *tmp; + int is_new; if (!test_bit(CACHE_VALID, &old->flags)) { write_lock(&detail->hash_lock); @@ -113,9 +135,9 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail, set_bit(CACHE_NEGATIVE, &old->flags); else detail->update(old, new); - /* FIXME cache_fresh should come first */ + is_new = cache_fresh_locked(old, new->expiry_time); write_unlock(&detail->hash_lock); - cache_fresh(detail, old, new->expiry_time); + cache_fresh_unlocked(old, detail, is_new); return old; } write_unlock(&detail->hash_lock); @@ -138,9 +160,11 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail, tmp->next = *head; *head = tmp; cache_get(tmp); + is_new = cache_fresh_locked(tmp, new->expiry_time); + cache_fresh_locked(old, 0); write_unlock(&detail->hash_lock); - cache_fresh(detail, tmp, new->expiry_time); - cache_fresh(detail, old, 0); + cache_fresh_unlocked(tmp, detail, is_new); + cache_fresh_unlocked(old, detail, 0); detail->cache_put(old, detail); return tmp; } @@ -192,7 +216,8 @@ int cache_check(struct cache_detail *detail, clear_bit(CACHE_PENDING, &h->flags); if (rv == -EAGAIN) { set_bit(CACHE_NEGATIVE, &h->flags); - cache_fresh(detail, h, get_seconds()+CACHE_NEW_EXPIRY); + cache_fresh_unlocked(h, detail, + cache_fresh_locked(h, get_seconds()+CACHE_NEW_EXPIRY)); rv = -ENOENT; } break; @@ -213,22 +238,6 @@ int cache_check(struct cache_detail *detail, return rv; } -static void queue_loose(struct cache_detail *detail, struct cache_head *ch); - -void cache_fresh(struct cache_detail *detail, - struct cache_head *head, time_t expiry) -{ - - head->expiry_time = expiry; - head->last_refresh = get_seconds(); - if (!test_and_set_bit(CACHE_VALID, &head->flags)) - cache_revisit_request(head); - if (test_and_clear_bit(CACHE_PENDING, &head->flags)) { - cache_revisit_request(head); - queue_loose(detail, head); - } -} - /* * caches need to be periodically cleaned. * For this we maintain a list of cache_detail and diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 40401196e7de..69b8238f3d10 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -105,7 +105,6 @@ EXPORT_SYMBOL(auth_unix_lookup); EXPORT_SYMBOL(cache_check); EXPORT_SYMBOL(cache_flush); EXPORT_SYMBOL(cache_purge); -EXPORT_SYMBOL(cache_fresh); EXPORT_SYMBOL(cache_init); EXPORT_SYMBOL(cache_register); EXPORT_SYMBOL(cache_unregister); -- cgit v1.2.3 From baab935ff3bdac20c558809da0d8e8f761840219 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 27 Mar 2006 01:15:09 -0800 Subject: [PATCH] knfsd: Convert sunrpc_cache to use krefs .. it makes some of the code nicer. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/export.c | 51 ++++++++++++++++++--------------------- fs/nfsd/nfs4idmap.c | 18 ++++++-------- fs/nfsd/nfsfh.c | 2 +- include/linux/nfsd/export.h | 4 +-- include/linux/sunrpc/cache.h | 13 +++++----- net/sunrpc/auth_gss/svcauth_gss.c | 28 +++++++++------------ net/sunrpc/cache.c | 20 +++++++-------- net/sunrpc/svcauth_unix.c | 20 +++++++-------- 8 files changed, 72 insertions(+), 84 deletions(-) (limited to 'include') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index abd68965822f..cc811a1094cb 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -57,18 +57,17 @@ static int exp_verify_string(char *cp, int max); #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) static struct cache_head *expkey_table[EXPKEY_HASHMAX]; -void expkey_put(struct cache_head *item, struct cache_detail *cd) +void expkey_put(struct kref *ref) { - if (cache_put(item, cd)) { - struct svc_expkey *key = container_of(item, struct svc_expkey, h); - if (test_bit(CACHE_VALID, &item->flags) && - !test_bit(CACHE_NEGATIVE, &item->flags)) { - dput(key->ek_dentry); - mntput(key->ek_mnt); - } - auth_domain_put(key->ek_client); - kfree(key); + struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); + + if (test_bit(CACHE_VALID, &key->h.flags) && + !test_bit(CACHE_NEGATIVE, &key->h.flags)) { + dput(key->ek_dentry); + mntput(key->ek_mnt); } + auth_domain_put(key->ek_client); + kfree(key); } static void expkey_request(struct cache_detail *cd, @@ -158,7 +157,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) set_bit(CACHE_NEGATIVE, &key.h.flags); ek = svc_expkey_update(&key, ek); if (ek) - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); else err = -ENOMEM; } else { struct nameidata nd; @@ -172,7 +171,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) ek = svc_expkey_update(&key, ek); if (ek) - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); else err = -ENOMEM; path_release(&nd); @@ -318,15 +317,13 @@ svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old) static struct cache_head *export_table[EXPORT_HASHMAX]; -void svc_export_put(struct cache_head *item, struct cache_detail *cd) +static void svc_export_put(struct kref *ref) { - if (cache_put(item, cd)) { - struct svc_export *exp = container_of(item, struct svc_export, h); - dput(exp->ex_dentry); - mntput(exp->ex_mnt); - auth_domain_put(exp->ex_client); - kfree(exp); - } + struct svc_export *exp = container_of(ref, struct svc_export, h.ref); + dput(exp->ex_dentry); + mntput(exp->ex_mnt); + auth_domain_put(exp->ex_client); + kfree(exp); } static void svc_export_request(struct cache_detail *cd, @@ -633,7 +630,7 @@ static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv, if (ek) ek = svc_expkey_update(&key,ek); if (ek) { - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); return 0; } return -ENOMEM; @@ -762,7 +759,7 @@ static void exp_fsid_unhash(struct svc_export *exp) ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid); if (ek && !IS_ERR(ek)) { ek->h.expiry_time = get_seconds()-1; - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); } svc_expkey_cache.nextcheck = get_seconds(); } @@ -800,7 +797,7 @@ static void exp_unhash(struct svc_export *exp) ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino); if (ek && !IS_ERR(ek)) { ek->h.expiry_time = get_seconds()-1; - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); } svc_expkey_cache.nextcheck = get_seconds(); } @@ -902,7 +899,7 @@ finish: if (exp) exp_put(exp); if (fsid_key && !IS_ERR(fsid_key)) - expkey_put(&fsid_key->h, &svc_expkey_cache); + cache_put(&fsid_key->h, &svc_expkey_cache); if (clp) auth_domain_put(clp); path_release(&nd); @@ -1030,7 +1027,7 @@ exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, return ERR_PTR(PTR_ERR(ek)); exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp); - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); if (!exp || IS_ERR(exp)) return ERR_PTR(PTR_ERR(exp)); @@ -1068,7 +1065,7 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, else rv = fh_compose(fhp, exp, fsid_key->ek_dentry, NULL); - expkey_put(&fsid_key->h, &svc_expkey_cache); + cache_put(&fsid_key->h, &svc_expkey_cache); return rv; } @@ -1187,7 +1184,7 @@ static int e_show(struct seq_file *m, void *p) cache_get(&exp->h); if (cache_check(&svc_export_cache, &exp->h, NULL)) return 0; - if (cache_put(&exp->h, &svc_export_cache)) BUG(); + cache_put(&exp->h, &svc_export_cache); return svc_export_show(m, &svc_export_cache, cp); } diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 75cfbb68b205..4b6aa60dfceb 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -96,12 +96,10 @@ ent_init(struct cache_head *cnew, struct cache_head *citm) } static void -ent_put(struct cache_head *ch, struct cache_detail *cd) +ent_put(struct kref *ref) { - if (cache_put(ch, cd)) { - struct ent *map = container_of(ch, struct ent, h); - kfree(map); - } + struct ent *map = container_of(ref, struct ent, h.ref); + kfree(map); } static struct cache_head * @@ -270,7 +268,7 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen) if (res == NULL) goto out; - ent_put(&res->h, &idtoname_cache); + cache_put(&res->h, &idtoname_cache); error = 0; out: @@ -433,7 +431,7 @@ nametoid_parse(struct cache_detail *cd, char *buf, int buflen) if (res == NULL) goto out; - ent_put(&res->h, &nametoid_cache); + cache_put(&res->h, &nametoid_cache); error = 0; out: kfree(buf1); @@ -562,7 +560,7 @@ do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *), goto out_put; return 0; out_put: - ent_put(&(*item)->h, detail); + cache_put(&(*item)->h, detail); out_err: *item = NULL; return ret; @@ -613,7 +611,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen if (ret) return ret; *id = item->id; - ent_put(&item->h, &nametoid_cache); + cache_put(&item->h, &nametoid_cache); return 0; } @@ -635,7 +633,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) ret = strlen(item->name); BUG_ON(ret > IDMAP_NAMESZ); memcpy(name, item->name, ret); - ent_put(&item->h, &idtoname_cache); + cache_put(&item->h, &idtoname_cache); return ret; } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 7a3e397b4ed3..3f2ec2e6d06c 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -506,7 +506,7 @@ fh_put(struct svc_fh *fhp) nfsd_nr_put++; } if (exp) { - svc_export_put(&exp->h, &svc_export_cache); + cache_put(&exp->h, &svc_export_cache); fhp->fh_export = NULL; } return; diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index d52e0b7ad37b..a6c08a47b25c 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -102,13 +102,11 @@ int exp_rootfh(struct auth_domain *, int exp_pseudoroot(struct auth_domain *, struct svc_fh *fhp, struct cache_req *creq); int nfserrno(int errno); -extern void expkey_put(struct cache_head *item, struct cache_detail *cd); -extern void svc_export_put(struct cache_head *item, struct cache_detail *cd); extern struct cache_detail svc_export_cache, svc_expkey_cache; static inline void exp_put(struct svc_export *exp) { - svc_export_put(&exp->h, &svc_export_cache); + cache_put(&exp->h, &svc_export_cache); } static inline void exp_get(struct svc_export *exp) diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index a37fead1873b..ad3f5cbdb770 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -50,7 +50,7 @@ struct cache_head { time_t last_refresh; /* If CACHE_PENDING, this is when upcall * was sent, else this is when update was received */ - atomic_t refcnt; + struct kref ref; unsigned long flags; }; #define CACHE_VALID 0 /* Entry contains valid data */ @@ -68,8 +68,7 @@ struct cache_detail { atomic_t inuse; /* active user-space update or lookup */ char *name; - void (*cache_put)(struct cache_head *, - struct cache_detail*); + void (*cache_put)(struct kref *); void (*cache_request)(struct cache_detail *cd, struct cache_head *h, @@ -151,17 +150,17 @@ extern void cache_clean_deferred(void *owner); static inline struct cache_head *cache_get(struct cache_head *h) { - atomic_inc(&h->refcnt); + kref_get(&h->ref); return h; } -static inline int cache_put(struct cache_head *h, struct cache_detail *cd) +static inline void cache_put(struct cache_head *h, struct cache_detail *cd) { - if (atomic_read(&h->refcnt) <= 2 && + if (atomic_read(&h->ref.refcount) <= 2 && h->expiry_time < cd->nextcheck) cd->nextcheck = h->expiry_time; - return atomic_dec_and_test(&h->refcnt); + kref_put(&h->ref, cd->cache_put); } extern void cache_init(struct cache_head *h); diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 380152603d1e..4d7eb9e704da 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -89,13 +89,11 @@ static void rsi_free(struct rsi *rsii) kfree(rsii->out_token.data); } -static void rsi_put(struct cache_head *item, struct cache_detail *cd) +static void rsi_put(struct kref *ref) { - struct rsi *rsii = container_of(item, struct rsi, h); - if (cache_put(item, cd)) { - rsi_free(rsii); - kfree(rsii); - } + struct rsi *rsii = container_of(ref, struct rsi, h.ref); + rsi_free(rsii); + kfree(rsii); } static inline int rsi_hash(struct rsi *item) @@ -267,7 +265,7 @@ static int rsi_parse(struct cache_detail *cd, out: rsi_free(&rsii); if (rsip) - rsi_put(&rsip->h, &rsi_cache); + cache_put(&rsip->h, &rsi_cache); else status = -ENOMEM; return status; @@ -357,14 +355,12 @@ static void rsc_free(struct rsc *rsci) put_group_info(rsci->cred.cr_group_info); } -static void rsc_put(struct cache_head *item, struct cache_detail *cd) +static void rsc_put(struct kref *ref) { - struct rsc *rsci = container_of(item, struct rsc, h); + struct rsc *rsci = container_of(ref, struct rsc, h.ref); - if (cache_put(item, cd)) { - rsc_free(rsci); - kfree(rsci); - } + rsc_free(rsci); + kfree(rsci); } static inline int @@ -509,7 +505,7 @@ static int rsc_parse(struct cache_detail *cd, out: rsc_free(&rsci); if (rscp) - rsc_put(&rscp->h, &rsc_cache); + cache_put(&rscp->h, &rsc_cache); else status = -ENOMEM; return status; @@ -1076,7 +1072,7 @@ drop: ret = SVC_DROP; out: if (rsci) - rsc_put(&rsci->h, &rsc_cache); + cache_put(&rsci->h, &rsc_cache); return ret; } @@ -1168,7 +1164,7 @@ out_err: put_group_info(rqstp->rq_cred.cr_group_info); rqstp->rq_cred.cr_group_info = NULL; if (gsd->rsci) - rsc_put(&gsd->rsci->h, &rsc_cache); + cache_put(&gsd->rsci->h, &rsc_cache); gsd->rsci = NULL; return stat; diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index edcda4fd88e8..dd81e5928172 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -42,7 +42,7 @@ void cache_init(struct cache_head *h) time_t now = get_seconds(); h->next = NULL; h->flags = 0; - atomic_set(&h->refcnt, 1); + kref_init(&h->ref); h->expiry_time = now + CACHE_NEW_EXPIRY; h->last_refresh = now; } @@ -81,7 +81,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, if (detail->match(tmp, key)) { cache_get(tmp); write_unlock(&detail->hash_lock); - detail->cache_put(new, detail); + cache_put(new, detail); return tmp; } } @@ -145,7 +145,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail, /* We need to insert a new entry */ tmp = detail->alloc(); if (!tmp) { - detail->cache_put(old, detail); + cache_put(old, detail); return NULL; } cache_init(tmp); @@ -165,7 +165,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail, write_unlock(&detail->hash_lock); cache_fresh_unlocked(tmp, detail, is_new); cache_fresh_unlocked(old, detail, 0); - detail->cache_put(old, detail); + cache_put(old, detail); return tmp; } EXPORT_SYMBOL(sunrpc_cache_update); @@ -234,7 +234,7 @@ int cache_check(struct cache_detail *detail, cache_defer_req(rqstp, h); if (rv) - detail->cache_put(h, detail); + cache_put(h, detail); return rv; } @@ -431,7 +431,7 @@ static int cache_clean(void) if (test_and_clear_bit(CACHE_PENDING, &ch->flags)) queue_loose(current_detail, ch); - if (atomic_read(&ch->refcnt) == 1) + if (atomic_read(&ch->ref.refcount) == 1) break; } if (ch) { @@ -446,7 +446,7 @@ static int cache_clean(void) current_index ++; spin_unlock(&cache_list_lock); if (ch) - d->cache_put(ch, d); + cache_put(ch, d); } else spin_unlock(&cache_list_lock); @@ -723,7 +723,7 @@ cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) !test_bit(CACHE_PENDING, &rq->item->flags)) { list_del(&rq->q.list); spin_unlock(&queue_lock); - cd->cache_put(rq->item, cd); + cache_put(rq->item, cd); kfree(rq->buf); kfree(rq); } else @@ -906,7 +906,7 @@ static void queue_loose(struct cache_detail *detail, struct cache_head *ch) continue; list_del(&cr->q.list); spin_unlock(&queue_lock); - detail->cache_put(cr->item, detail); + cache_put(cr->item, detail); kfree(cr->buf); kfree(cr); return; @@ -1192,7 +1192,7 @@ static int c_show(struct seq_file *m, void *p) ifdebug(CACHE) seq_printf(m, "# expiry=%ld refcnt=%d flags=%lx\n", - cp->expiry_time, atomic_read(&cp->refcnt), cp->flags); + cp->expiry_time, atomic_read(&cp->ref.refcount), cp->flags); cache_get(cp); if (cache_check(cd, cp, NULL)) /* cache_check does a cache_put on failure */ diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 7e38621a20b7..11020c0b7db5 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -84,15 +84,15 @@ struct ip_map { }; static struct cache_head *ip_table[IP_HASHMAX]; -static void ip_map_put(struct cache_head *item, struct cache_detail *cd) +static void ip_map_put(struct kref *kref) { + struct cache_head *item = container_of(kref, struct cache_head, ref); struct ip_map *im = container_of(item, struct ip_map,h); - if (cache_put(item, cd)) { - if (test_bit(CACHE_VALID, &item->flags) && - !test_bit(CACHE_NEGATIVE, &item->flags)) - auth_domain_put(&im->m_client->h); - kfree(im); - } + + if (test_bit(CACHE_VALID, &item->flags) && + !test_bit(CACHE_NEGATIVE, &item->flags)) + auth_domain_put(&im->m_client->h); + kfree(im); } #if IP_HASHBITS == 8 @@ -315,7 +315,7 @@ static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t ex hash_ip((unsigned long)ipm->m_addr.s_addr)); if (!ch) return -ENOMEM; - ip_map_put(ch, &ip_map_cache); + cache_put(ch, &ip_map_cache); return 0; } @@ -369,7 +369,7 @@ struct auth_domain *auth_unix_lookup(struct in_addr addr) rv = &ipm->m_client->h; kref_get(&rv->ref); } - ip_map_put(&ipm->h, &ip_map_cache); + cache_put(&ipm->h, &ip_map_cache); return rv; } @@ -403,7 +403,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) case 0: rqstp->rq_client = &ipm->m_client->h; kref_get(&rqstp->rq_client->ref); - ip_map_put(&ipm->h, &ip_map_cache); + cache_put(&ipm->h, &ip_map_cache); break; } return SVC_OK; -- cgit v1.2.3 From 74cae61ab45f19a3e8c4d9f53c0e94df129c7915 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 27 Mar 2006 01:15:10 -0800 Subject: [PATCH] fs/nfsd/export.c,net/sunrpc/cache.c: make needlessly global code static We can now make some code static. Signed-off-by: Adrian Bunk Cc: Neil Brown Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfsd/export.c | 13 ++++++++----- include/linux/nfsd/export.h | 5 +---- include/linux/sunrpc/cache.h | 1 - net/sunrpc/cache.c | 2 +- net/sunrpc/sunrpc_syms.c | 1 - 5 files changed, 10 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index cc811a1094cb..c340be0a3f59 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -57,7 +57,7 @@ static int exp_verify_string(char *cp, int max); #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) static struct cache_head *expkey_table[EXPKEY_HASHMAX]; -void expkey_put(struct kref *ref) +static void expkey_put(struct kref *ref) { struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); @@ -87,6 +87,8 @@ static void expkey_request(struct cache_detail *cd, static struct svc_expkey *svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old); static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *); +static struct cache_detail svc_expkey_cache; + static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) { /* client fsidtype fsid [path] */ @@ -255,7 +257,7 @@ static struct cache_head *expkey_alloc(void) return NULL; } -struct cache_detail svc_expkey_cache = { +static struct cache_detail svc_expkey_cache = { .owner = THIS_MODULE, .hash_size = EXPKEY_HASHMAX, .hash_table = expkey_table, @@ -345,7 +347,8 @@ static void svc_export_request(struct cache_detail *cd, (*bpp)[-1] = '\n'; } -struct svc_export *svc_export_update(struct svc_export *new, struct svc_export *old); +static struct svc_export *svc_export_update(struct svc_export *new, + struct svc_export *old); static struct svc_export *svc_export_lookup(struct svc_export *); static int check_export(struct inode *inode, int flags) @@ -574,7 +577,7 @@ svc_export_lookup(struct svc_export *exp) return NULL; } -struct svc_export * +static struct svc_export * svc_export_update(struct svc_export *new, struct svc_export *old) { struct cache_head *ch; @@ -593,7 +596,7 @@ svc_export_update(struct svc_export *new, struct svc_export *old) } -struct svc_expkey * +static struct svc_expkey * exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) { struct svc_expkey key, *ek; diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index a6c08a47b25c..d2a8abb5011a 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -86,9 +86,6 @@ void nfsd_export_shutdown(void); void nfsd_export_flush(void); void exp_readlock(void); void exp_readunlock(void); -struct svc_expkey * exp_find_key(struct auth_domain *clp, - int fsid_type, u32 *fsidv, - struct cache_req *reqp); struct svc_export * exp_get_by_name(struct auth_domain *clp, struct vfsmount *mnt, struct dentry *dentry, @@ -102,7 +99,7 @@ int exp_rootfh(struct auth_domain *, int exp_pseudoroot(struct auth_domain *, struct svc_fh *fhp, struct cache_req *creq); int nfserrno(int errno); -extern struct cache_detail svc_export_cache, svc_expkey_cache; +extern struct cache_detail svc_export_cache; static inline void exp_put(struct svc_export *exp) { diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index ad3f5cbdb770..b5612c958cce 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -163,7 +163,6 @@ static inline void cache_put(struct cache_head *h, struct cache_detail *cd) kref_put(&h->ref, cd->cache_put); } -extern void cache_init(struct cache_head *h); extern int cache_check(struct cache_detail *detail, struct cache_head *h, struct cache_req *rqstp); extern void cache_flush(void); diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index dd81e5928172..3ac4193a78ed 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -37,7 +37,7 @@ static void cache_defer_req(struct cache_req *req, struct cache_head *item); static void cache_revisit_request(struct cache_head *item); -void cache_init(struct cache_head *h) +static void cache_init(struct cache_head *h) { time_t now = get_seconds(); h->next = NULL; diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 69b8238f3d10..769114f0f886 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -105,7 +105,6 @@ EXPORT_SYMBOL(auth_unix_lookup); EXPORT_SYMBOL(cache_check); EXPORT_SYMBOL(cache_flush); EXPORT_SYMBOL(cache_purge); -EXPORT_SYMBOL(cache_init); EXPORT_SYMBOL(cache_register); EXPORT_SYMBOL(cache_unregister); EXPORT_SYMBOL(qword_add); -- cgit v1.2.3 From 1e9f28fa1eb9773bf65bae08288c6a0a38eef4a7 Mon Sep 17 00:00:00 2001 From: "Siddha, Suresh B" Date: Mon, 27 Mar 2006 01:15:22 -0800 Subject: [PATCH] sched: new sched domain for representing multi-core Add a new sched domain for representing multi-core with shared caches between cores. Consider a dual package system, each package containing two cores and with last level cache shared between cores with in a package. If there are two runnable processes, with this appended patch those two processes will be scheduled on different packages. On such systems, with this patch we have observed 8% perf improvement with specJBB(2 warehouse) benchmark and 35% improvement with CFP2000 rate(with 2 users). This new domain will come into play only on multi-core systems with shared caches. On other systems, this sched domain will be removed by domain degeneration code. This new domain can be also used for implementing power savings policy (see OLS 2005 CMP kernel scheduler paper for more details.. I will post another patch for power savings policy soon) Most of the arch/* file changes are for cpu_coregroup_map() implementation. Signed-off-by: Suresh Siddha Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/Kconfig | 9 +++++ arch/i386/kernel/cpu/common.c | 10 +++-- arch/i386/kernel/cpu/intel_cacheinfo.c | 22 +++++++++- arch/i386/kernel/smpboot.c | 24 +++++++++++ arch/x86_64/Kconfig | 9 +++++ arch/x86_64/kernel/setup.c | 3 +- arch/x86_64/kernel/smpboot.c | 24 +++++++++++ include/asm-i386/processor.h | 5 +++ include/asm-i386/topology.h | 2 + include/asm-x86_64/processor.h | 4 ++ include/asm-x86_64/smp.h | 1 + include/asm-x86_64/topology.h | 2 + include/linux/topology.h | 9 +++++ kernel/sched.c | 73 +++++++++++++++++++++++++++++++--- 14 files changed, 186 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index f7db71d0b913..f17bd1d2707e 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -231,6 +231,15 @@ config SCHED_SMT cost of slightly increased overhead in some places. If unsure say N here. +config SCHED_MC + bool "Multi-core scheduler support" + depends on SMP + default y + help + Multi-core scheduler support improves the CPU scheduler's decision + making when dealing with multi-core CPU chips at a cost of slightly + increased overhead in some places. If unsure say N here. + source "kernel/Kconfig.preempt" config X86_UP_APIC diff --git a/arch/i386/kernel/cpu/common.c b/arch/i386/kernel/cpu/common.c index 7e3d6b6a4e96..a06a49075f10 100644 --- a/arch/i386/kernel/cpu/common.c +++ b/arch/i386/kernel/cpu/common.c @@ -266,7 +266,7 @@ static void __init early_cpu_detect(void) void __cpuinit generic_identify(struct cpuinfo_x86 * c) { u32 tfms, xlvl; - int junk; + int ebx; if (have_cpuid_p()) { /* Get vendor name */ @@ -282,7 +282,7 @@ void __cpuinit generic_identify(struct cpuinfo_x86 * c) /* Intel-defined flags: level 0x00000001 */ if ( c->cpuid_level >= 0x00000001 ) { u32 capability, excap; - cpuid(0x00000001, &tfms, &junk, &excap, &capability); + cpuid(0x00000001, &tfms, &ebx, &excap, &capability); c->x86_capability[0] = capability; c->x86_capability[4] = excap; c->x86 = (tfms >> 8) & 15; @@ -292,6 +292,11 @@ void __cpuinit generic_identify(struct cpuinfo_x86 * c) if (c->x86 >= 0x6) c->x86_model += ((tfms >> 16) & 0xF) << 4; c->x86_mask = tfms & 15; +#ifdef CONFIG_SMP + c->apicid = phys_pkg_id((ebx >> 24) & 0xFF, 0); +#else + c->apicid = (ebx >> 24) & 0xFF; +#endif } else { /* Have CPUID level 0 only - unheard of */ c->x86 = 4; @@ -474,7 +479,6 @@ void __cpuinit detect_ht(struct cpuinfo_x86 *c) cpuid(1, &eax, &ebx, &ecx, &edx); - c->apicid = phys_pkg_id((ebx >> 24) & 0xFF, 0); if (!cpu_has(c, X86_FEATURE_HT) || cpu_has(c, X86_FEATURE_CMP_LEGACY)) return; diff --git a/arch/i386/kernel/cpu/intel_cacheinfo.c b/arch/i386/kernel/cpu/intel_cacheinfo.c index ce61921369e5..7e7fd4e67dd0 100644 --- a/arch/i386/kernel/cpu/intel_cacheinfo.c +++ b/arch/i386/kernel/cpu/intel_cacheinfo.c @@ -173,6 +173,10 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */ unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */ unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */ + unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb; +#ifdef CONFIG_SMP + unsigned int cpu = (c == &boot_cpu_data) ? 0 : (c - cpu_data); +#endif if (c->cpuid_level > 3) { static int is_initialized; @@ -205,9 +209,15 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) break; case 2: new_l2 = this_leaf.size/1024; + num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; + index_msb = get_count_order(num_threads_sharing); + l2_id = c->apicid >> index_msb; break; case 3: new_l3 = this_leaf.size/1024; + num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; + index_msb = get_count_order(num_threads_sharing); + l3_id = c->apicid >> index_msb; break; default: break; @@ -273,11 +283,19 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) if (new_l1i) l1i = new_l1i; - if (new_l2) + if (new_l2) { l2 = new_l2; +#ifdef CONFIG_SMP + cpu_llc_id[cpu] = l2_id; +#endif + } - if (new_l3) + if (new_l3) { l3 = new_l3; +#ifdef CONFIG_SMP + cpu_llc_id[cpu] = l3_id; +#endif + } if ( trace ) printk (KERN_INFO "CPU: Trace cache: %dK uops", trace); diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 82371d83bfa9..a6969903f2d6 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -72,6 +72,9 @@ int phys_proc_id[NR_CPUS] __read_mostly = {[0 ... NR_CPUS-1] = BAD_APICID}; /* Core ID of each logical CPU */ int cpu_core_id[NR_CPUS] __read_mostly = {[0 ... NR_CPUS-1] = BAD_APICID}; +/* Last level cache ID of each logical CPU */ +int cpu_llc_id[NR_CPUS] __cpuinitdata = {[0 ... NR_CPUS-1] = BAD_APICID}; + /* representing HT siblings of each logical CPU */ cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly; EXPORT_SYMBOL(cpu_sibling_map); @@ -440,6 +443,18 @@ static void __devinit smp_callin(void) static int cpucount; +/* maps the cpu to the sched domain representing multi-core */ +cpumask_t cpu_coregroup_map(int cpu) +{ + struct cpuinfo_x86 *c = cpu_data + cpu; + /* + * For perf, we return last level cache shared map. + * TBD: when power saving sched policy is added, we will return + * cpu_core_map when power saving policy is enabled + */ + return c->llc_shared_map; +} + /* representing cpus for which sibling maps can be computed */ static cpumask_t cpu_sibling_setup_map; @@ -459,12 +474,16 @@ set_cpu_sibling_map(int cpu) cpu_set(cpu, cpu_sibling_map[i]); cpu_set(i, cpu_core_map[cpu]); cpu_set(cpu, cpu_core_map[i]); + cpu_set(i, c[cpu].llc_shared_map); + cpu_set(cpu, c[i].llc_shared_map); } } } else { cpu_set(cpu, cpu_sibling_map[cpu]); } + cpu_set(cpu, c[cpu].llc_shared_map); + if (current_cpu_data.x86_max_cores == 1) { cpu_core_map[cpu] = cpu_sibling_map[cpu]; c[cpu].booted_cores = 1; @@ -472,6 +491,11 @@ set_cpu_sibling_map(int cpu) } for_each_cpu_mask(i, cpu_sibling_setup_map) { + if (cpu_llc_id[cpu] != BAD_APICID && + cpu_llc_id[cpu] == cpu_llc_id[i]) { + cpu_set(i, c[cpu].llc_shared_map); + cpu_set(cpu, c[i].llc_shared_map); + } if (phys_proc_id[cpu] == phys_proc_id[i]) { cpu_set(i, cpu_core_map[cpu]); cpu_set(cpu, cpu_core_map[i]); diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 45efe0ca88f8..1cb4aa241c8c 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -250,6 +250,15 @@ config SCHED_SMT cost of slightly increased overhead in some places. If unsure say N here. +config SCHED_MC + bool "Multi-core scheduler support" + depends on SMP + default y + help + Multi-core scheduler support improves the CPU scheduler's decision + making when dealing with multi-core CPU chips at a cost of slightly + increased overhead in some places. If unsure say N here. + source "kernel/Kconfig.preempt" config NUMA diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c index a57eec8311a7..d1f3e9272c05 100644 --- a/arch/x86_64/kernel/setup.c +++ b/arch/x86_64/kernel/setup.c @@ -962,7 +962,6 @@ static void __cpuinit detect_ht(struct cpuinfo_x86 *c) cpuid(1, &eax, &ebx, &ecx, &edx); - c->apicid = phys_pkg_id(0); if (!cpu_has(c, X86_FEATURE_HT) || cpu_has(c, X86_FEATURE_CMP_LEGACY)) return; @@ -1171,6 +1170,8 @@ void __cpuinit identify_cpu(struct cpuinfo_x86 *c) c->x86_capability[2] = cpuid_edx(0x80860001); } + c->apicid = phys_pkg_id(0); + /* * Vendor-specific initialization. In this section we * canonicalize the feature flags, meaning if there are diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c index 66e98659d077..ea48fa638070 100644 --- a/arch/x86_64/kernel/smpboot.c +++ b/arch/x86_64/kernel/smpboot.c @@ -68,6 +68,9 @@ u8 phys_proc_id[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = BAD_APICID }; /* core ID of each logical CPU */ u8 cpu_core_id[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = BAD_APICID }; +/* Last level cache ID of each logical CPU */ +u8 cpu_llc_id[NR_CPUS] __cpuinitdata = {[0 ... NR_CPUS-1] = BAD_APICID}; + /* Bitmask of currently online CPUs */ cpumask_t cpu_online_map __read_mostly; @@ -445,6 +448,18 @@ void __cpuinit smp_callin(void) cpu_set(cpuid, cpu_callin_map); } +/* maps the cpu to the sched domain representing multi-core */ +cpumask_t cpu_coregroup_map(int cpu) +{ + struct cpuinfo_x86 *c = cpu_data + cpu; + /* + * For perf, we return last level cache shared map. + * TBD: when power saving sched policy is added, we will return + * cpu_core_map when power saving policy is enabled + */ + return c->llc_shared_map; +} + /* representing cpus for which sibling maps can be computed */ static cpumask_t cpu_sibling_setup_map; @@ -463,12 +478,16 @@ static inline void set_cpu_sibling_map(int cpu) cpu_set(cpu, cpu_sibling_map[i]); cpu_set(i, cpu_core_map[cpu]); cpu_set(cpu, cpu_core_map[i]); + cpu_set(i, c[cpu].llc_shared_map); + cpu_set(cpu, c[i].llc_shared_map); } } } else { cpu_set(cpu, cpu_sibling_map[cpu]); } + cpu_set(cpu, c[cpu].llc_shared_map); + if (current_cpu_data.x86_max_cores == 1) { cpu_core_map[cpu] = cpu_sibling_map[cpu]; c[cpu].booted_cores = 1; @@ -476,6 +495,11 @@ static inline void set_cpu_sibling_map(int cpu) } for_each_cpu_mask(i, cpu_sibling_setup_map) { + if (cpu_llc_id[cpu] != BAD_APICID && + cpu_llc_id[cpu] == cpu_llc_id[i]) { + cpu_set(i, c[cpu].llc_shared_map); + cpu_set(cpu, c[i].llc_shared_map); + } if (phys_proc_id[cpu] == phys_proc_id[i]) { cpu_set(i, cpu_core_map[cpu]); cpu_set(cpu, cpu_core_map[i]); diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index feca5d961e2b..af4bfd012475 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -20,6 +20,7 @@ #include #include #include +#include /* flag for disabling the tsc */ extern int tsc_disable; @@ -67,6 +68,9 @@ struct cpuinfo_x86 { char pad0; int x86_power; unsigned long loops_per_jiffy; +#ifdef CONFIG_SMP + cpumask_t llc_shared_map; /* cpus sharing the last level cache */ +#endif unsigned char x86_max_cores; /* cpuid returned max cores value */ unsigned char booted_cores; /* number of cores as seen by OS */ unsigned char apicid; @@ -103,6 +107,7 @@ extern struct cpuinfo_x86 cpu_data[]; extern int phys_proc_id[NR_CPUS]; extern int cpu_core_id[NR_CPUS]; +extern int cpu_llc_id[NR_CPUS]; extern char ignore_fpu_irq; extern void identify_cpu(struct cpuinfo_x86 *); diff --git a/include/asm-i386/topology.h b/include/asm-i386/topology.h index aa958c6ee83e..b94e5eeef917 100644 --- a/include/asm-i386/topology.h +++ b/include/asm-i386/topology.h @@ -112,4 +112,6 @@ extern unsigned long node_remap_size[]; #endif /* CONFIG_NUMA */ +extern cpumask_t cpu_coregroup_map(int cpu); + #endif /* _ASM_I386_TOPOLOGY_H */ diff --git a/include/asm-x86_64/processor.h b/include/asm-x86_64/processor.h index 8c8d88c036ed..1aa2cee43344 100644 --- a/include/asm-x86_64/processor.h +++ b/include/asm-x86_64/processor.h @@ -20,6 +20,7 @@ #include #include #include +#include #define TF_MASK 0x00000100 #define IF_MASK 0x00000200 @@ -65,6 +66,9 @@ struct cpuinfo_x86 { __u32 x86_power; __u32 extended_cpuid_level; /* Max extended CPUID function supported */ unsigned long loops_per_jiffy; +#ifdef CONFIG_SMP + cpumask_t llc_shared_map; /* cpus sharing the last level cache */ +#endif __u8 apicid; __u8 booted_cores; /* number of cores as seen by OS */ } ____cacheline_aligned; diff --git a/include/asm-x86_64/smp.h b/include/asm-x86_64/smp.h index 9ccbb2cfd5c0..a4fdaeb5c397 100644 --- a/include/asm-x86_64/smp.h +++ b/include/asm-x86_64/smp.h @@ -56,6 +56,7 @@ extern cpumask_t cpu_sibling_map[NR_CPUS]; extern cpumask_t cpu_core_map[NR_CPUS]; extern u8 phys_proc_id[NR_CPUS]; extern u8 cpu_core_id[NR_CPUS]; +extern u8 cpu_llc_id[NR_CPUS]; #define SMP_TRAMPOLINE_BASE 0x6000 diff --git a/include/asm-x86_64/topology.h b/include/asm-x86_64/topology.h index c642f5d9882d..9db54e9d17bb 100644 --- a/include/asm-x86_64/topology.h +++ b/include/asm-x86_64/topology.h @@ -68,4 +68,6 @@ extern int __node_distance(int, int); #include +extern cpumask_t cpu_coregroup_map(int cpu); + #endif diff --git a/include/linux/topology.h b/include/linux/topology.h index e8eb0040ce3a..a305ae2e44b6 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -164,6 +164,15 @@ .nr_balance_failed = 0, \ } +#ifdef CONFIG_SCHED_MC +#ifndef SD_MC_INIT +/* for now its same as SD_CPU_INIT. + * TBD: Tune Domain parameters! + */ +#define SD_MC_INIT SD_CPU_INIT +#endif +#endif + #ifdef CONFIG_NUMA #ifndef SD_NODE_INIT #error Please define an appropriate SD_NODE_INIT in include/asm/topology.h!!! diff --git a/kernel/sched.c b/kernel/sched.c index a96a05d23262..8a8b71b5751b 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5574,11 +5574,31 @@ static int cpu_to_cpu_group(int cpu) } #endif +#ifdef CONFIG_SCHED_MC +static DEFINE_PER_CPU(struct sched_domain, core_domains); +static struct sched_group sched_group_core[NR_CPUS]; +#endif + +#if defined(CONFIG_SCHED_MC) && defined(CONFIG_SCHED_SMT) +static int cpu_to_core_group(int cpu) +{ + return first_cpu(cpu_sibling_map[cpu]); +} +#elif defined(CONFIG_SCHED_MC) +static int cpu_to_core_group(int cpu) +{ + return cpu; +} +#endif + static DEFINE_PER_CPU(struct sched_domain, phys_domains); static struct sched_group sched_group_phys[NR_CPUS]; static int cpu_to_phys_group(int cpu) { -#ifdef CONFIG_SCHED_SMT +#if defined(CONFIG_SCHED_MC) + cpumask_t mask = cpu_coregroup_map(cpu); + return first_cpu(mask); +#elif defined(CONFIG_SCHED_SMT) return first_cpu(cpu_sibling_map[cpu]); #else return cpu; @@ -5676,6 +5696,17 @@ void build_sched_domains(const cpumask_t *cpu_map) sd->parent = p; sd->groups = &sched_group_phys[group]; +#ifdef CONFIG_SCHED_MC + p = sd; + sd = &per_cpu(core_domains, i); + group = cpu_to_core_group(i); + *sd = SD_MC_INIT; + sd->span = cpu_coregroup_map(i); + cpus_and(sd->span, sd->span, *cpu_map); + sd->parent = p; + sd->groups = &sched_group_core[group]; +#endif + #ifdef CONFIG_SCHED_SMT p = sd; sd = &per_cpu(cpu_domains, i); @@ -5701,6 +5732,19 @@ void build_sched_domains(const cpumask_t *cpu_map) } #endif +#ifdef CONFIG_SCHED_MC + /* Set up multi-core groups */ + for_each_cpu_mask(i, *cpu_map) { + cpumask_t this_core_map = cpu_coregroup_map(i); + cpus_and(this_core_map, this_core_map, *cpu_map); + if (i != first_cpu(this_core_map)) + continue; + init_sched_build_groups(sched_group_core, this_core_map, + &cpu_to_core_group); + } +#endif + + /* Set up physical groups */ for (i = 0; i < MAX_NUMNODES; i++) { cpumask_t nodemask = node_to_cpumask(i); @@ -5797,11 +5841,31 @@ void build_sched_domains(const cpumask_t *cpu_map) power = SCHED_LOAD_SCALE; sd->groups->cpu_power = power; #endif +#ifdef CONFIG_SCHED_MC + sd = &per_cpu(core_domains, i); + power = SCHED_LOAD_SCALE + (cpus_weight(sd->groups->cpumask)-1) + * SCHED_LOAD_SCALE / 10; + sd->groups->cpu_power = power; + + sd = &per_cpu(phys_domains, i); + /* + * This has to be < 2 * SCHED_LOAD_SCALE + * Lets keep it SCHED_LOAD_SCALE, so that + * while calculating NUMA group's cpu_power + * we can simply do + * numa_group->cpu_power += phys_group->cpu_power; + * + * See "only add power once for each physical pkg" + * comment below + */ + sd->groups->cpu_power = SCHED_LOAD_SCALE; +#else sd = &per_cpu(phys_domains, i); power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * (cpus_weight(sd->groups->cpumask)-1) / 10; sd->groups->cpu_power = power; +#endif #ifdef CONFIG_NUMA sd = &per_cpu(allnodes_domains, i); @@ -5823,7 +5887,6 @@ void build_sched_domains(const cpumask_t *cpu_map) next_sg: for_each_cpu_mask(j, sg->cpumask) { struct sched_domain *sd; - int power; sd = &per_cpu(phys_domains, j); if (j != first_cpu(sd->groups->cpumask)) { @@ -5833,10 +5896,8 @@ next_sg: */ continue; } - power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * - (cpus_weight(sd->groups->cpumask)-1) / 10; - sg->cpu_power += power; + sg->cpu_power += sd->groups->cpu_power; } sg = sg->next; if (sg != sched_group_nodes[i]) @@ -5849,6 +5910,8 @@ next_sg: struct sched_domain *sd; #ifdef CONFIG_SCHED_SMT sd = &per_cpu(cpu_domains, i); +#elif defined(CONFIG_SCHED_MC) + sd = &per_cpu(core_domains, i); #else sd = &per_cpu(phys_domains, i); #endif -- cgit v1.2.3 From a117e66ed45ac0569c039ea60bd7a9a61e031858 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:25 -0800 Subject: [PATCH] unify pfn_to_page: generic functions There are 3 memory models, FLATMEM, DISCONTIGMEM, SPARSEMEM. Each arch has its own page_to_pfn(), pfn_to_page() for each models. But most of them can use the same arithmetic. This patch adds asm-generic/memory_model.h, which includes generic page_to_pfn(), pfn_to_page() definitions for each memory model. When CONFIG_OUT_OF_LINE_PFN_TO_PAGE=y, out-of-line functions are used instead of macro. This is enabled by some archs and reduces text size. Signed-off-by: KAMEZAWA Hiroyuki Cc: Hugh Dickins Cc: Andi Kleen Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Cc: Richard Henderson Cc: Ivan Kokshaysky Cc: Russell King Cc: Ian Molton Cc: Mikael Starvik Cc: David Howells Cc: Yoshinori Sato Cc: Hirokazu Takata Cc: Ralf Baechle Cc: Kyle McMartin Cc: Heiko Carstens Cc: Martin Schwidefsky Cc: Paul Mundt Cc: Kazumoto Kojima Cc: Richard Curnow Cc: William Lee Irwin III Cc: "David S. Miller" Cc: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Cc: Miles Bader Cc: Chris Zankel Cc: "Luck, Tony" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/memory_model.h | 77 ++++++++++++++++++++++++++++++++++++++ include/asm-sparc64/page.h | 2 + include/linux/mmzone.h | 11 ------ mm/page_alloc.c | 42 +++++++++++++++++++++ 4 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 include/asm-generic/memory_model.h (limited to 'include') diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h new file mode 100644 index 000000000000..a7bb4978e808 --- /dev/null +++ b/include/asm-generic/memory_model.h @@ -0,0 +1,77 @@ +#ifndef __ASM_MEMORY_MODEL_H +#define __ASM_MEMORY_MODEL_H + +#ifdef __KERNEL__ +#ifndef __ASSEMBLY__ + +#if defined(CONFIG_FLATMEM) + +#ifndef ARCH_PFN_OFFSET +#define ARCH_PFN_OFFSET (0UL) +#endif + +#elif defined(CONFIG_DISCONTIGMEM) + +#ifndef arch_pfn_to_nid +#define arch_pfn_to_nid(pfn) pfn_to_nid(pfn) +#endif + +#ifndef arch_local_page_offset +#define arch_local_page_offset(pfn, nid) \ + ((pfn) - NODE_DATA(nid)->node_start_pfn) +#endif + +#endif /* CONFIG_DISCONTIGMEM */ + +#ifdef CONFIG_OUT_OF_LINE_PFN_TO_PAGE +struct page; +/* this is useful when inlined pfn_to_page is too big */ +extern struct page *pfn_to_page(unsigned long pfn); +extern unsigned long page_to_pfn(struct page *page); +#else +/* + * supports 3 memory models. + */ +#if defined(CONFIG_FLATMEM) + +#define pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) +#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + \ + ARCH_PFN_OFFSET) +#elif defined(CONFIG_DISCONTIGMEM) + +#define pfn_to_page(pfn) \ +({ unsigned long __pfn = (pfn); \ + unsigned long __nid = arch_pfn_to_nid(pfn); \ + NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\ +}) + +#define page_to_pfn(pg) \ +({ struct page *__pg = (pg); \ + struct zone *__zone = page_zone(__pg); \ + (unsigned long)(__pg - __zone->zone_mem_map) + \ + __zone->zone_start_pfn; \ +}) + +#elif defined(CONFIG_SPARSEMEM) +/* + * Note: section's mem_map is encorded to reflect its start_pfn. + * section[i].section_mem_map == mem_map's address - start_pfn; + */ +#define page_to_pfn(pg) \ +({ struct page *__pg = (pg); \ + int __sec = page_to_section(__pg); \ + __pg - __section_mem_map_addr(__nr_to_section(__sec)); \ +}) + +#define pfn_to_page(pfn) \ +({ unsigned long __pfn = (pfn); \ + struct mem_section *__sec = __pfn_to_section(__pfn); \ + __section_mem_map_addr(__sec) + __pfn; \ +}) +#endif /* CONFIG_FLATMEM/DISCONTIGMEM/SPARSEMEM */ +#endif /* CONFIG_OUT_OF_LINE_PFN_TO_PAGE */ + +#endif /* __ASSEMBLY__ */ +#endif /* __KERNEL__ */ + +#endif diff --git a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h index 66fe4ac59fd6..aabb21906724 100644 --- a/include/asm-sparc64/page.h +++ b/include/asm-sparc64/page.h @@ -111,6 +111,8 @@ typedef unsigned long pgprot_t; (_AC(0x0000000070000000,UL)) : \ (_AC(0xfffff80000000000,UL) + (1UL << 32UL))) +#include + #endif /* !(__ASSEMBLY__) */ /* to align the pointer to the (next) page boundary */ diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index ebfc238cc243..0c1c0c0cce65 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -602,17 +602,6 @@ static inline struct mem_section *__pfn_to_section(unsigned long pfn) return __nr_to_section(pfn_to_section_nr(pfn)); } -#define pfn_to_page(pfn) \ -({ \ - unsigned long __pfn = (pfn); \ - __section_mem_map_addr(__pfn_to_section(__pfn)) + __pfn; \ -}) -#define page_to_pfn(page) \ -({ \ - page - __section_mem_map_addr(__nr_to_section( \ - page_to_section(page))); \ -}) - static inline int pfn_valid(unsigned long pfn) { if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 338a02bb004d..349b328763b7 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2745,3 +2745,45 @@ void *__init alloc_large_system_hash(const char *tablename, return table; } + +#ifdef CONFIG_OUT_OF_LINE_PFN_TO_PAGE +/* + * pfn <-> page translation. out-of-line version. + * (see asm-generic/memory_model.h) + */ +#if defined(CONFIG_FLATMEM) +struct page *pfn_to_page(unsigned long pfn) +{ + return mem_map + (pfn - ARCH_PFN_OFFSET); +} +unsigned long page_to_pfn(struct page *page) +{ + return (page - mem_map) + ARCH_PFN_OFFSET; +} +#elif defined(CONFIG_DISCONTIGMEM) +struct page *pfn_to_page(unsigned long pfn) +{ + int nid = arch_pfn_to_nid(pfn); + return NODE_DATA(nid)->node_mem_map + arch_local_page_offset(pfn,nid); +} +unsigned long page_to_pfn(struct page *page) +{ + struct zone *zone = page_zone(page); + return (page - zone->zone_mem_map) + zone->zone_start_pfn; + +} +#elif defined(CONFIG_SPARSEMEM) +struct page *pfn_to_page(unsigned long pfn) +{ + return __section_mem_map_addr(__pfn_to_section(pfn)) + pfn; +} + +unsigned long page_to_pfn(struct page *page) +{ + long section_id = page_to_section(page); + return page - __section_mem_map_addr(__nr_to_section(section_id)); +} +#endif /* CONFIG_FLATMEM/DISCONTIGMME/SPARSEMEM */ +EXPORT_SYMBOL(pfn_to_page); +EXPORT_SYMBOL(page_to_pfn); +#endif /* CONFIG_OUT_OF_LINE_PFN_TO_PAGE */ -- cgit v1.2.3 From ad658b385e6308066f9084a7ea01305b223cd3a0 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:33 -0800 Subject: [PATCH] unify pfn_to_page: i386 pfn_to_page i386 can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-i386/mmzone.h | 17 ----------------- include/asm-i386/page.h | 3 +-- 2 files changed, 1 insertion(+), 19 deletions(-) (limited to 'include') diff --git a/include/asm-i386/mmzone.h b/include/asm-i386/mmzone.h index 74f595d80579..e33e9f9e4c66 100644 --- a/include/asm-i386/mmzone.h +++ b/include/asm-i386/mmzone.h @@ -70,8 +70,6 @@ static inline int pfn_to_nid(unsigned long pfn) #endif } -#define node_localnr(pfn, nid) ((pfn) - node_data[nid]->node_start_pfn) - /* * Following are macros that each numa implmentation must define. */ @@ -86,21 +84,6 @@ static inline int pfn_to_nid(unsigned long pfn) /* XXX: FIXME -- wli */ #define kern_addr_valid(kaddr) (0) -#define pfn_to_page(pfn) \ -({ \ - unsigned long __pfn = pfn; \ - int __node = pfn_to_nid(__pfn); \ - &NODE_DATA(__node)->node_mem_map[node_localnr(__pfn,__node)]; \ -}) - -#define page_to_pfn(pg) \ -({ \ - struct page *__page = pg; \ - struct zone *__zone = page_zone(__page); \ - (unsigned long)(__page - __zone->zone_mem_map) \ - + __zone->zone_start_pfn; \ -}) - #ifdef CONFIG_X86_NUMAQ /* we have contiguous memory on NUMA-Q */ #define pfn_valid(pfn) ((pfn) < num_physpages) #else diff --git a/include/asm-i386/page.h b/include/asm-i386/page.h index 997ca5d17876..30f52a2263ba 100644 --- a/include/asm-i386/page.h +++ b/include/asm-i386/page.h @@ -126,8 +126,6 @@ extern int page_is_ram(unsigned long pagenr); #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) #ifdef CONFIG_FLATMEM -#define pfn_to_page(pfn) (mem_map + (pfn)) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define pfn_valid(pfn) ((pfn) < max_mapnr) #endif /* CONFIG_FLATMEM */ #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) @@ -141,6 +139,7 @@ extern int page_is_ram(unsigned long pagenr); #endif /* __KERNEL__ */ +#include #include #endif /* _I386_PAGE_H */ -- cgit v1.2.3 From dc8ecb43701a78bd3c38e7fed1d1c76840579450 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:34 -0800 Subject: [PATCH] unify pfn_to_page: x86_64 pfn_to_page x86_64 can use generic funcs. For DISCONTIGMEM, CONFIG_OUT_OF_LINE_PFN_TO_PAGE is selected. Signed-off-by: KAMEZAWA Hiroyuki Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86_64/Kconfig | 4 ++++ arch/x86_64/mm/numa.c | 15 --------------- include/asm-x86_64/mmzone.h | 4 ---- include/asm-x86_64/page.h | 3 +-- 4 files changed, 5 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 1cb4aa241c8c..4310b4a311a5 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -334,6 +334,10 @@ config HAVE_ARCH_EARLY_PFN_TO_NID def_bool y depends on NUMA +config OUT_OF_LINE_PFN_TO_PAGE + def_bool y + depends on DISCONTIGMEM + config NR_CPUS int "Maximum number of CPUs (2-256)" range 2 255 diff --git a/arch/x86_64/mm/numa.c b/arch/x86_64/mm/numa.c index 63c72641b737..4be82d6e2b48 100644 --- a/arch/x86_64/mm/numa.c +++ b/arch/x86_64/mm/numa.c @@ -377,21 +377,6 @@ EXPORT_SYMBOL(node_data); * Should do that. */ -/* Requires pfn_valid(pfn) to be true */ -struct page *pfn_to_page(unsigned long pfn) -{ - int nid = phys_to_nid(((unsigned long)(pfn)) << PAGE_SHIFT); - return (pfn - node_start_pfn(nid)) + NODE_DATA(nid)->node_mem_map; -} -EXPORT_SYMBOL(pfn_to_page); - -unsigned long page_to_pfn(struct page *page) -{ - return (long)(((page) - page_zone(page)->zone_mem_map) + - page_zone(page)->zone_start_pfn); -} -EXPORT_SYMBOL(page_to_pfn); - int pfn_valid(unsigned long pfn) { unsigned nid; diff --git a/include/asm-x86_64/mmzone.h b/include/asm-x86_64/mmzone.h index 937f99b26883..6b18cd8f293d 100644 --- a/include/asm-x86_64/mmzone.h +++ b/include/asm-x86_64/mmzone.h @@ -44,12 +44,8 @@ static inline __attribute__((pure)) int phys_to_nid(unsigned long addr) #define pfn_to_nid(pfn) phys_to_nid((unsigned long)(pfn) << PAGE_SHIFT) #define kvaddr_to_nid(kaddr) phys_to_nid(__pa(kaddr)) -extern struct page *pfn_to_page(unsigned long pfn); -extern unsigned long page_to_pfn(struct page *page); extern int pfn_valid(unsigned long pfn); #endif -#define local_mapnr(kvaddr) \ - ( (__pa(kvaddr) >> PAGE_SHIFT) - node_start_pfn(kvaddr_to_nid(kvaddr)) ) #endif #endif diff --git a/include/asm-x86_64/page.h b/include/asm-x86_64/page.h index 615e3e494929..408185bac351 100644 --- a/include/asm-x86_64/page.h +++ b/include/asm-x86_64/page.h @@ -123,8 +123,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; #define __boot_va(x) __va(x) #define __boot_pa(x) __pa(x) #ifdef CONFIG_FLATMEM -#define pfn_to_page(pfn) (mem_map + (pfn)) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define pfn_valid(pfn) ((pfn) < end_pfn) #endif @@ -140,6 +138,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; #endif /* __KERNEL__ */ +#include #include #endif /* _X86_64_PAGE_H */ -- cgit v1.2.3 From 659e35051b165fb40f1160190c2d23f3d5c319d5 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:35 -0800 Subject: [PATCH] unify pfn_to_page: powerpc pfn_to_page PowerPC can use generic ones. Signed-off-by: KAMEZAWA Hiroyuki Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-powerpc/page.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-powerpc/page.h b/include/asm-powerpc/page.h index 0b82df483f7f..2fbecebe1c92 100644 --- a/include/asm-powerpc/page.h +++ b/include/asm-powerpc/page.h @@ -69,8 +69,6 @@ #endif #ifdef CONFIG_FLATMEM -#define pfn_to_page(pfn) (mem_map + (pfn)) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define pfn_valid(pfn) ((pfn) < max_mapnr) #endif @@ -200,6 +198,7 @@ extern void copy_user_page(void *to, void *from, unsigned long vaddr, struct page *p); extern int page_is_ram(unsigned long pfn); +#include #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ -- cgit v1.2.3 From 1c05dda2b6f025267ab79a267e0a84628a3760e1 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:36 -0800 Subject: [PATCH] unify pfn_to_page: alpha pfn_to_page Alpha can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Richard Henderson Cc: Ivan Kokshaysky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-alpha/mmzone.h | 16 +--------------- include/asm-alpha/page.h | 4 +--- 2 files changed, 2 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/asm-alpha/mmzone.h b/include/asm-alpha/mmzone.h index a011ef4cf3d3..c9004398f273 100644 --- a/include/asm-alpha/mmzone.h +++ b/include/asm-alpha/mmzone.h @@ -59,9 +59,6 @@ PLAT_NODE_DATA_LOCALNR(unsigned long p, int n) #define kvaddr_to_nid(kaddr) pa_to_nid(__pa(kaddr)) #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) -#define local_mapnr(kvaddr) \ - ((__pa(kvaddr) >> PAGE_SHIFT) - node_start_pfn(kvaddr_to_nid(kvaddr))) - /* * Given a kaddr, LOCAL_BASE_ADDR finds the owning node of the memory * and returns the kaddr corresponding to first physical page in the @@ -104,19 +101,8 @@ PLAT_NODE_DATA_LOCALNR(unsigned long p, int n) __xx; \ }) -#define pfn_to_page(pfn) \ -({ \ - unsigned long kaddr = (unsigned long)__va((pfn) << PAGE_SHIFT); \ - (NODE_DATA(kvaddr_to_nid(kaddr))->node_mem_map + local_mapnr(kaddr)); \ -}) - -#define page_to_pfn(page) \ - ((page) - page_zone(page)->zone_mem_map + \ - (page_zone(page)->zone_start_pfn)) - #define page_to_pa(page) \ - ((( (page) - page_zone(page)->zone_mem_map ) \ - + page_zone(page)->zone_start_pfn) << PAGE_SHIFT) + (page_to_pfn(page) << PAGE_SHIFT) #define pfn_to_nid(pfn) pa_to_nid(((u64)(pfn) << PAGE_SHIFT)) #define pfn_valid(pfn) \ diff --git a/include/asm-alpha/page.h b/include/asm-alpha/page.h index fa0b41b164a7..61bcf70b5eac 100644 --- a/include/asm-alpha/page.h +++ b/include/asm-alpha/page.h @@ -85,8 +85,6 @@ typedef unsigned long pgprot_t; #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) #ifndef CONFIG_DISCONTIGMEM -#define pfn_to_page(pfn) (mem_map + (pfn)) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define pfn_valid(pfn) ((pfn) < max_mapnr) @@ -95,9 +93,9 @@ typedef unsigned long pgprot_t; #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) - #endif /* __KERNEL__ */ +#include #include #endif /* _ALPHA_PAGE_H */ -- cgit v1.2.3 From 7eb98a2f3b605d8a11434d855b58d828393ea533 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:37 -0800 Subject: [PATCH] unify pfn_to_page: arm pfn_to_page ARM can use generic funcs. PFN_TO_NID, LOCAL_MAP_NR are defined by sub-archs. Signed-off-by: KAMEZAWA Hirotuki Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-arm/memory.h | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/asm-arm/memory.h b/include/asm-arm/memory.h index b4e1146ab682..afa5c3ea077c 100644 --- a/include/asm-arm/memory.h +++ b/include/asm-arm/memory.h @@ -172,9 +172,7 @@ static inline __deprecated void *bus_to_virt(unsigned long x) * virt_addr_valid(k) indicates whether a virtual address is valid */ #ifndef CONFIG_DISCONTIGMEM - -#define page_to_pfn(page) (((page) - mem_map) + PHYS_PFN_OFFSET) -#define pfn_to_page(pfn) ((mem_map + (pfn)) - PHYS_PFN_OFFSET) +#define ARCH_PFN_OFFSET (PHYS_PFN_OFFSET) #define pfn_valid(pfn) ((pfn) >= PHYS_PFN_OFFSET && (pfn) < (PHYS_PFN_OFFSET + max_mapnr)) #define virt_to_page(kaddr) (pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)) @@ -189,13 +187,8 @@ static inline __deprecated void *bus_to_virt(unsigned long x) * around in memory. */ #include - -#define page_to_pfn(page) \ - (( (page) - page_zone(page)->zone_mem_map) \ - + page_zone(page)->zone_start_pfn) - -#define pfn_to_page(pfn) \ - (PFN_TO_MAPBASE(pfn) + LOCAL_MAP_NR((pfn) << PAGE_SHIFT)) +#define arch_pfn_to_nid(pfn) (PFN_TO_NID(pfn)) +#define arch_local_page_offset(pfn, nid) (LOCAL_MAP_NR((pfn) << PAGE_OFFSET)) #define pfn_valid(pfn) \ ({ \ @@ -243,4 +236,6 @@ static inline __deprecated void *bus_to_virt(unsigned long x) #endif +#include + #endif -- cgit v1.2.3 From 89fccaf2c4a4a8318153e3460a92284dadeb8f71 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:38 -0800 Subject: [PATCH] unify pfn_to_page: arm26 pfn_to_page arm26 can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Ian Molton Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-arm26/memory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-arm26/memory.h b/include/asm-arm26/memory.h index 20d78616f650..a65f10b80dfb 100644 --- a/include/asm-arm26/memory.h +++ b/include/asm-arm26/memory.h @@ -81,8 +81,7 @@ static inline void *phys_to_virt(unsigned long x) * virt_to_page(k) convert a _valid_ virtual address to struct page * * virt_addr_valid(k) indicates whether a virtual address is valid */ -#define page_to_pfn(page) (((page) - mem_map) + PHYS_PFN_OFFSET) -#define pfn_to_page(pfn) ((mem_map + (pfn)) - PHYS_PFN_OFFSET) +#define ARCH_PFN_OFFSET (PHYS_PFN_OFFSET) #define pfn_valid(pfn) ((pfn) >= PHYS_PFN_OFFSET && (pfn) < (PHYS_PFN_OFFSET + max_mapnr)) #define virt_to_page(kaddr) (pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)) @@ -98,4 +97,5 @@ static inline void *phys_to_virt(unsigned long x) */ #define page_to_bus(page) (page_address(page)) +#include #endif -- cgit v1.2.3 From bb872f787a9b6a40a4aa206175e768137f595d9c Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:39 -0800 Subject: [PATCH] unify pfn_to_page: cris pfn_to_page cris can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Mikael Starvik Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-cris/page.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-cris/page.h b/include/asm-cris/page.h index c99c478c482f..3787633e6209 100644 --- a/include/asm-cris/page.h +++ b/include/asm-cris/page.h @@ -43,8 +43,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; /* On CRIS the PFN numbers doesn't start at 0 so we have to compensate */ /* for that before indexing into the page table starting at mem_map */ -#define pfn_to_page(pfn) (mem_map + ((pfn) - (PAGE_OFFSET >> PAGE_SHIFT))) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + (PAGE_OFFSET >> PAGE_SHIFT)) +#define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) #define pfn_valid(pfn) (((pfn) - (PAGE_OFFSET >> PAGE_SHIFT)) < max_mapnr) /* to index into the page map. our pages all start at physical addr PAGE_OFFSET so @@ -77,6 +76,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; #endif /* __KERNEL__ */ +#include #include #endif /* _CRIS_PAGE_H */ -- cgit v1.2.3 From 5cdac7ca1b727184b1af21effaffcb9e2cd535c2 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:40 -0800 Subject: [PATCH] unify pfn_to_page: FRV pfn_to_page FRV can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-frv/page.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/asm-frv/page.h b/include/asm-frv/page.h index b8221b611b5c..dc0f7e08a4c2 100644 --- a/include/asm-frv/page.h +++ b/include/asm-frv/page.h @@ -57,13 +57,9 @@ extern unsigned long min_low_pfn; extern unsigned long max_pfn; #ifdef CONFIG_MMU -#define pfn_to_page(pfn) (mem_map + (pfn)) -#define page_to_pfn(page) ((unsigned long) ((page) - mem_map)) #define pfn_valid(pfn) ((pfn) < max_mapnr) - #else -#define pfn_to_page(pfn) (&mem_map[(pfn) - (PAGE_OFFSET >> PAGE_SHIFT)]) -#define page_to_pfn(page) ((PAGE_OFFSET >> PAGE_SHIFT) + (unsigned long) ((page) - mem_map)) +#define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) #define pfn_valid(pfn) ((pfn) >= min_low_pfn && (pfn) < max_low_pfn) #endif @@ -87,6 +83,7 @@ extern unsigned long max_pfn; #define WANT_PAGE_VIRTUAL 1 #endif +#include #include #endif /* _ASM_PAGE_H */ -- cgit v1.2.3 From dd6cc7631cf2a63b3d6c32619054e017479e72ca Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:41 -0800 Subject: [PATCH] unify pfn_to_page: h8300 pfn_to_page H8300 can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Yoshinori Sato Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-h8300/page.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-h8300/page.h b/include/asm-h8300/page.h index cd35b1cc6cde..6472c9f88227 100644 --- a/include/asm-h8300/page.h +++ b/include/asm-h8300/page.h @@ -71,8 +71,7 @@ extern unsigned long memory_end; #define page_to_virt(page) ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) #define pfn_valid(page) (page < max_mapnr) -#define pfn_to_page(pfn) virt_to_page(pfn_to_virt(pfn)) -#define page_to_pfn(page) virt_to_pfn(page_to_virt(page)) +#define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) #define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ ((void *)(kaddr) < (void *)memory_end)) @@ -81,6 +80,7 @@ extern unsigned long memory_end; #endif /* __KERNEL__ */ +#include #include #endif /* _H8300_PAGE_H */ -- cgit v1.2.3 From 7126cffe74f0dbcd015aaabab01069f422526535 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:42 -0800 Subject: [PATCH] unify pfn_to_page: m32r pfn_to_page m32r can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Hirokazu Takata Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-m32r/mmzone.h | 14 -------------- include/asm-m32r/page.h | 5 ++--- 2 files changed, 2 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/asm-m32r/mmzone.h b/include/asm-m32r/mmzone.h index adc7970a77ec..9f3b5accda88 100644 --- a/include/asm-m32r/mmzone.h +++ b/include/asm-m32r/mmzone.h @@ -21,20 +21,6 @@ extern struct pglist_data *node_data[]; __pgdat->node_start_pfn + __pgdat->node_spanned_pages - 1; \ }) -#define pfn_to_page(pfn) \ -({ \ - unsigned long __pfn = pfn; \ - int __node = pfn_to_nid(__pfn); \ - &NODE_DATA(__node)->node_mem_map[node_localnr(__pfn,__node)]; \ -}) - -#define page_to_pfn(pg) \ -({ \ - struct page *__page = pg; \ - struct zone *__zone = page_zone(__page); \ - (unsigned long)(__page - __zone->zone_mem_map) \ - + __zone->zone_start_pfn; \ -}) #define pmd_page(pmd) (pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT)) /* * pfn_valid should be made as fast as possible, and the current definition diff --git a/include/asm-m32r/page.h b/include/asm-m32r/page.h index 4ab578876361..9ddbc087dbc5 100644 --- a/include/asm-m32r/page.h +++ b/include/asm-m32r/page.h @@ -76,9 +76,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; #ifndef CONFIG_DISCONTIGMEM #define PFN_BASE (CONFIG_MEMORY_START >> PAGE_SHIFT) -#define pfn_to_page(pfn) (mem_map + ((pfn) - PFN_BASE)) -#define page_to_pfn(page) \ - ((unsigned long)((page) - mem_map) + PFN_BASE) +#define ARCH_PFN_OFFSET PFN_BASE #define pfn_valid(pfn) (((pfn) - PFN_BASE) < max_mapnr) #endif /* !CONFIG_DISCONTIGMEM */ @@ -92,6 +90,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; #endif /* __KERNEL__ */ +#include #include #endif /* _ASM_M32R_PAGE_H */ -- cgit v1.2.3 From a02036e796e5046fe0463b9a092e9b617c525866 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:43 -0800 Subject: [PATCH] unify pfn_to_page: mips pfn_to_page MIPS can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-mips/mmzone.h | 14 -------------- include/asm-mips/page.h | 3 +-- 2 files changed, 1 insertion(+), 16 deletions(-) (limited to 'include') diff --git a/include/asm-mips/mmzone.h b/include/asm-mips/mmzone.h index 011caebac369..7bde4432092b 100644 --- a/include/asm-mips/mmzone.h +++ b/include/asm-mips/mmzone.h @@ -22,20 +22,6 @@ NODE_DATA(__n)->node_spanned_pages) : 0);\ }) -#define pfn_to_page(pfn) \ -({ \ - unsigned long __pfn = (pfn); \ - pg_data_t *__pg = NODE_DATA(pfn_to_nid(__pfn)); \ - __pg->node_mem_map + (__pfn - __pg->node_start_pfn); \ -}) - -#define page_to_pfn(p) \ -({ \ - struct page *__p = (p); \ - struct zone *__z = page_zone(__p); \ - ((__p - __z->zone_mem_map) + __z->zone_start_pfn); \ -}) - /* XXX: FIXME -- wli */ #define kern_addr_valid(addr) (0) diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index ee25a779bf49..a1eab136ff6c 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -140,8 +140,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) #ifndef CONFIG_NEED_MULTIPLE_NODES -#define pfn_to_page(pfn) (mem_map + (pfn)) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define pfn_valid(pfn) ((pfn) < max_mapnr) #endif @@ -160,6 +158,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; #define WANT_PAGE_VIRTUAL #endif +#include #include #endif /* _ASM_PAGE_H */ -- cgit v1.2.3 From 0d833b41092df2f4a65cbdb0a0a947c35cdd2f9d Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:43 -0800 Subject: [PATCH] unify pfn_to_page: parisc pfn_to_page PARISC can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Kyle McMartin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-parisc/mmzone.h | 17 ----------------- include/asm-parisc/page.h | 3 +-- 2 files changed, 1 insertion(+), 19 deletions(-) (limited to 'include') diff --git a/include/asm-parisc/mmzone.h b/include/asm-parisc/mmzone.h index ae039f4fd711..ceb9b73199d1 100644 --- a/include/asm-parisc/mmzone.h +++ b/include/asm-parisc/mmzone.h @@ -25,23 +25,6 @@ extern struct node_map_data node_data[]; pg_data_t *__pgdat = NODE_DATA(nid); \ __pgdat->node_start_pfn + __pgdat->node_spanned_pages; \ }) -#define node_localnr(pfn, nid) ((pfn) - node_start_pfn(nid)) - -#define pfn_to_page(pfn) \ -({ \ - unsigned long __pfn = (pfn); \ - int __node = pfn_to_nid(__pfn); \ - &NODE_DATA(__node)->node_mem_map[node_localnr(__pfn,__node)]; \ -}) - -#define page_to_pfn(pg) \ -({ \ - struct page *__page = pg; \ - struct zone *__zone = page_zone(__page); \ - BUG_ON(__zone == NULL); \ - (unsigned long)(__page - __zone->zone_mem_map) \ - + __zone->zone_start_pfn; \ -}) /* We have these possible memory map layouts: * Astro: 0-3.75, 67.75-68, 4-64 diff --git a/include/asm-parisc/page.h b/include/asm-parisc/page.h index 4a6752b0afed..9f303c0c3cd7 100644 --- a/include/asm-parisc/page.h +++ b/include/asm-parisc/page.h @@ -130,8 +130,6 @@ extern int npmem_ranges; #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) #ifndef CONFIG_DISCONTIGMEM -#define pfn_to_page(pfn) (mem_map + (pfn)) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define pfn_valid(pfn) ((pfn) < max_mapnr) #endif /* CONFIG_DISCONTIGMEM */ @@ -152,6 +150,7 @@ extern int npmem_ranges; #endif /* __KERNEL__ */ +#include #include #endif /* _PARISC_PAGE_H */ -- cgit v1.2.3 From f68d4c996d3062c8fe64e3ed36d4c83d23288ad8 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:44 -0800 Subject: [PATCH] unify pfn_to_page: ppc pfn_to_page PPC can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-ppc/page.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-ppc/page.h b/include/asm-ppc/page.h index 538e0c8ab243..a70ba2ee552d 100644 --- a/include/asm-ppc/page.h +++ b/include/asm-ppc/page.h @@ -149,8 +149,7 @@ extern int page_is_ram(unsigned long pfn); #define __pa(x) ___pa((unsigned long)(x)) #define __va(x) ((void *)(___va((unsigned long)(x)))) -#define pfn_to_page(pfn) (mem_map + ((pfn) - PPC_PGSTART)) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + PPC_PGSTART) +#define ARCH_PFN_OFFSET (PPC_PGSTART) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define page_to_virt(page) __va(page_to_pfn(page) << PAGE_SHIFT) @@ -175,5 +174,6 @@ extern __inline__ int get_order(unsigned long size) /* We do define AT_SYSINFO_EHDR but don't use the gate mecanism */ #define __HAVE_ARCH_GATE_AREA 1 +#include #endif /* __KERNEL__ */ #endif /* _PPC_PAGE_H */ -- cgit v1.2.3 From aed630434c4fc0bca8ed62f6730d44ba00bdf595 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:45 -0800 Subject: [PATCH] unify pfn_to_page: s390 pfn_to_page s390 can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Heiko Carstens Cc: Martin Schwidefsky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-s390/page.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-s390/page.h b/include/asm-s390/page.h index 2430c561e021..3b1138ac7e79 100644 --- a/include/asm-s390/page.h +++ b/include/asm-s390/page.h @@ -181,8 +181,6 @@ page_get_storage_key(unsigned long addr) #define PAGE_OFFSET 0x0UL #define __pa(x) (unsigned long)(x) #define __va(x) (void *)(unsigned long)(x) -#define pfn_to_page(pfn) (mem_map + (pfn)) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map)) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define pfn_valid(pfn) ((pfn) < max_mapnr) @@ -193,6 +191,7 @@ page_get_storage_key(unsigned long addr) #endif /* __KERNEL__ */ +#include #include #endif /* _S390_PAGE_H */ -- cgit v1.2.3 From 104b8deaa5c0144cccfc7d914413ff80c7176af1 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:46 -0800 Subject: [PATCH] unify pfn_to_page: sh pfn_to_page sh can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Paul Mundt Cc: Kazumoto Kojima Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-sh/page.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h index 972c3f655b2a..9c89287c3e56 100644 --- a/include/asm-sh/page.h +++ b/include/asm-sh/page.h @@ -105,9 +105,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; /* PFN start number, because of __MEMORY_START */ #define PFN_START (__MEMORY_START >> PAGE_SHIFT) - -#define pfn_to_page(pfn) (mem_map + (pfn) - PFN_START) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + PFN_START) +#define ARCH_PFN_OFFSET (FPN_START) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define pfn_valid(pfn) (((pfn) - PFN_START) < max_mapnr) #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) @@ -117,6 +115,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; #endif /* __KERNEL__ */ +#include #include #endif /* __ASM_SH_PAGE_H */ -- cgit v1.2.3 From 309d34bbe521940de9098d306a58a1797a42d990 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:47 -0800 Subject: [PATCH] unify pfn_to_page: sh64 pfn_to_page sh64 can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Paul Mundt Cc: Richard Curnow Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-sh64/page.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/asm-sh64/page.h b/include/asm-sh64/page.h index c86df90f7cbd..e4937cdabebd 100644 --- a/include/asm-sh64/page.h +++ b/include/asm-sh64/page.h @@ -105,9 +105,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; /* PFN start number, because of __MEMORY_START */ #define PFN_START (__MEMORY_START >> PAGE_SHIFT) - -#define pfn_to_page(pfn) (mem_map + (pfn) - PFN_START) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + PFN_START) +#define ARCH_PFN_OFFSET (PFN_START) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define pfn_valid(pfn) (((pfn) - PFN_START) < max_mapnr) #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) @@ -117,6 +115,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; #endif /* __KERNEL__ */ +#include #include #endif /* __ASM_SH64_PAGE_H */ -- cgit v1.2.3 From 064b2a0762ef7dca5f7068a90b260a309ffe2002 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:48 -0800 Subject: [PATCH] unify pfn_to_page: sparc pfn_to_page sparc can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: William Lee Irwin III Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-sparc/page.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-sparc/page.h b/include/asm-sparc/page.h index 9122684f6c1e..ec3274b7ddf4 100644 --- a/include/asm-sparc/page.h +++ b/include/asm-sparc/page.h @@ -152,8 +152,7 @@ extern unsigned long pfn_base; #define virt_to_phys __pa #define phys_to_virt __va -#define pfn_to_page(pfn) (mem_map + ((pfn)-(pfn_base))) -#define page_to_pfn(page) ((unsigned long)(((page) - mem_map) + pfn_base)) +#define ARCH_PFN_OFFSET (pfn_base) #define virt_to_page(kaddr) (mem_map + ((((unsigned long)(kaddr)-PAGE_OFFSET)>>PAGE_SHIFT))) #define pfn_valid(pfn) (((pfn) >= (pfn_base)) && (((pfn)-(pfn_base)) < max_mapnr)) @@ -164,6 +163,7 @@ extern unsigned long pfn_base; #endif /* __KERNEL__ */ +#include #include #endif /* _SPARC_PAGE_H */ -- cgit v1.2.3 From 9828c185db7ab41426c03c5539b1759ccf3c28ed Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:50 -0800 Subject: [PATCH] unify pfn_to_page: uml pfn_to_page UML can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-um/page.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/asm-um/page.h b/include/asm-um/page.h index 0229814af31e..41364330aff1 100644 --- a/include/asm-um/page.h +++ b/include/asm-um/page.h @@ -106,9 +106,6 @@ extern unsigned long uml_physmem; #define __pa(virt) to_phys((void *) (unsigned long) (virt)) #define __va(phys) to_virt((unsigned long) (phys)) -#define page_to_pfn(page) ((page) - mem_map) -#define pfn_to_page(pfn) (mem_map + (pfn)) - #define phys_to_pfn(p) ((p) >> PAGE_SHIFT) #define pfn_to_phys(pfn) ((pfn) << PAGE_SHIFT) @@ -121,6 +118,7 @@ extern struct page *arch_validate(struct page *page, gfp_t mask, int order); extern void arch_free_page(struct page *page, int order); #define HAVE_ARCH_FREE_PAGE +#include #include #endif -- cgit v1.2.3 From e6009f1ba5f6ff0fc8723254ce89ef9c979cc370 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:52 -0800 Subject: [PATCH] unify pfn_to_page: v850 pfn_to_page v850 can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Miles Bader Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-v850/page.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-v850/page.h b/include/asm-v850/page.h index b4bc85e7b91a..ad03c46a1f92 100644 --- a/include/asm-v850/page.h +++ b/include/asm-v850/page.h @@ -111,8 +111,7 @@ typedef unsigned long pgprot_t; #define page_to_virt(page) \ ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) -#define pfn_to_page(pfn) virt_to_page (pfn_to_virt (pfn)) -#define page_to_pfn(page) virt_to_pfn (page_to_virt (page)) +#define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) #define pfn_valid(pfn) ((pfn) < max_mapnr) #define virt_addr_valid(kaddr) \ @@ -125,6 +124,7 @@ typedef unsigned long pgprot_t; #endif /* KERNEL */ +#include #include #endif /* __V850_PAGE_H__ */ -- cgit v1.2.3 From 655a0443470a73d5dc36e974a241e8db59bb1ccb Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:52 -0800 Subject: [PATCH] unify pfn_to_page: xtensa pfn_to_page xtensa can use generic funcs. Signed-off-by: KAMEZAWA Hiroyuki Cc: Chris Zankel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-xtensa/page.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/asm-xtensa/page.h b/include/asm-xtensa/page.h index 8ded36f255a2..992bac5c1258 100644 --- a/include/asm-xtensa/page.h +++ b/include/asm-xtensa/page.h @@ -109,10 +109,7 @@ void copy_user_page(void *to,void* from,unsigned long vaddr,struct page* page); #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) #define pfn_valid(pfn) ((unsigned long)pfn < max_mapnr) -#ifndef CONFIG_DISCONTIGMEM -# define pfn_to_page(pfn) (mem_map + (pfn)) -# define page_to_pfn(page) ((unsigned long)((page) - mem_map)) -#else +#ifdef CONFIG_DISCONTIGMEM # error CONFIG_DISCONTIGMEM not supported #endif @@ -130,4 +127,5 @@ void copy_user_page(void *to,void* from,unsigned long vaddr,struct page* page); VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) #endif /* __KERNEL__ */ +#include #endif /* _XTENSA_PAGE_H */ -- cgit v1.2.3 From 0ecd702bcb924d5fb7f687e09986f688336ac896 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:53 -0800 Subject: [PATCH] unify pfn_to_page: ia64 pfn_to_page ia64 has special config CONFIG_VIRTUAL_MEM_MAP. CONFIG_DISCONTIGMEM=y && CONFIG_VIRTUAL_MEM_MAP!=y is bug ? Signed-off-by: KAMEZAWA Hiroyuki Cc: "Luck, Tony" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-ia64/page.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h index 6e9aa23250c4..2087825eefa4 100644 --- a/include/asm-ia64/page.h +++ b/include/asm-ia64/page.h @@ -106,17 +106,25 @@ extern int ia64_pfn_valid (unsigned long pfn); # define ia64_pfn_valid(pfn) 1 #endif +#ifdef CONFIG_VIRTUAL_MEM_MAP +extern struct page *vmem_map; +#ifdef CONFIG_DISCONTIGMEM +# define page_to_pfn(page) ((unsigned long) (page - vmem_map)) +# define pfn_to_page(pfn) (vmem_map + (pfn)) +#endif +#endif + +#if defined(CONFIG_FLATMEM) || defined(CONFIG_SPARSEMEM) +/* FLATMEM always configures mem_map (mem_map = vmem_map if necessary) */ +#include +#endif + #ifdef CONFIG_FLATMEM # define pfn_valid(pfn) (((pfn) < max_mapnr) && ia64_pfn_valid(pfn)) -# define page_to_pfn(page) ((unsigned long) (page - mem_map)) -# define pfn_to_page(pfn) (mem_map + (pfn)) #elif defined(CONFIG_DISCONTIGMEM) -extern struct page *vmem_map; extern unsigned long min_low_pfn; extern unsigned long max_low_pfn; # define pfn_valid(pfn) (((pfn) >= min_low_pfn) && ((pfn) < max_low_pfn) && ia64_pfn_valid(pfn)) -# define page_to_pfn(page) ((unsigned long) (page - vmem_map)) -# define pfn_to_page(pfn) (vmem_map + (pfn)) #endif #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) -- cgit v1.2.3 From a0140c1d85637ee5f4ea7c78f066e3611a6a79dc Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:55 -0800 Subject: [PATCH] remove zone_mem_map This patch removes zone_mem_map. pfn_to_page uses pgdat, page_to_pfn uses zone. page_to_pfn can use pgdat instead of zone, which is only one user of zone_mem_map. By modifing it, we can remove zone_mem_map. Signed-off-by: KAMEZAWA Hiroyuki Cc: Dave Hansen Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-alpha/mmzone.h | 3 +-- include/asm-generic/memory_model.h | 10 +++++----- include/linux/mmzone.h | 1 - mm/page_alloc.c | 6 ++---- 4 files changed, 8 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/asm-alpha/mmzone.h b/include/asm-alpha/mmzone.h index c9004398f273..192d80c875b0 100644 --- a/include/asm-alpha/mmzone.h +++ b/include/asm-alpha/mmzone.h @@ -83,8 +83,7 @@ PLAT_NODE_DATA_LOCALNR(unsigned long p, int n) pte_t pte; \ unsigned long pfn; \ \ - pfn = ((unsigned long)((page)-page_zone(page)->zone_mem_map)) << 32; \ - pfn += page_zone(page)->zone_start_pfn << 32; \ + pfn = page_to_pfn(page) << 32; \ pte_val(pte) = pfn | pgprot_val(pgprot); \ \ pte; \ diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h index a7bb4978e808..0cfb086dd373 100644 --- a/include/asm-generic/memory_model.h +++ b/include/asm-generic/memory_model.h @@ -45,11 +45,11 @@ extern unsigned long page_to_pfn(struct page *page); NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\ }) -#define page_to_pfn(pg) \ -({ struct page *__pg = (pg); \ - struct zone *__zone = page_zone(__pg); \ - (unsigned long)(__pg - __zone->zone_mem_map) + \ - __zone->zone_start_pfn; \ +#define page_to_pfn(pg) \ +({ struct page *__pg = (pg); \ + struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg)); \ + (unsigned long)(__pg - __pgdat->node_mem_map) + \ + __pgdat->node_start_pfn; \ }) #elif defined(CONFIG_SPARSEMEM) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 0c1c0c0cce65..ace31c515a8c 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -225,7 +225,6 @@ struct zone { * Discontig memory support fields. */ struct pglist_data *zone_pgdat; - struct page *zone_mem_map; /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */ unsigned long zone_start_pfn; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 349b328763b7..8dc8f2735d22 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2042,7 +2042,6 @@ static __meminit void init_currently_empty_zone(struct zone *zone, zone_wait_table_init(zone, size); pgdat->nr_zones = zone_idx(zone) + 1; - zone->zone_mem_map = pfn_to_page(zone_start_pfn); zone->zone_start_pfn = zone_start_pfn; memmap_init(size, pgdat->node_id, zone_idx(zone), zone_start_pfn); @@ -2768,9 +2767,8 @@ struct page *pfn_to_page(unsigned long pfn) } unsigned long page_to_pfn(struct page *page) { - struct zone *zone = page_zone(page); - return (page - zone->zone_mem_map) + zone->zone_start_pfn; - + struct pglist_data *pgdat = NODE_DATA(page_to_nid(page)); + return (page - pgdat->node_mem_map) + pgdat->node_start_pfn; } #elif defined(CONFIG_SPARSEMEM) struct page *pfn_to_page(unsigned long pfn) -- cgit v1.2.3 From 8357f8695d58b50fbf2bd507b4b0fc2cd1e43bd6 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:57 -0800 Subject: [PATCH] define for_each_online_pgdat This patch defines for_each_online_pgdat() as a replacement of for_each_pgdat() Now, online nodes are managed by node_online_map. But for_each_pgdat() uses pgdat_link to iterate over all nodes(pgdat). This means management structure for online pgdat is duplicated. I think using node_online_map for for_each_pgdat() is simple and sane rather ather than pgdat_link. New macro is named as for_each_online_pgdat(). Following patch will fix callers of for_each_pgdat(). The bootmem allocater uses for_each_pgdat() before pgdat initialization. I don't think it's sane. Following patch will fix it. Signed-off-by: Yasunori Goto Signed-off-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 108 +++++++++++++++++++++++++---------------------- include/linux/nodemask.h | 4 ++ 2 files changed, 61 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index ace31c515a8c..96eb08025092 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -13,6 +13,7 @@ #include #include #include +#include #include /* Free memory management - zoned buddy allocator. */ @@ -349,57 +350,6 @@ unsigned long __init node_memmap_size_bytes(int, unsigned long, unsigned long); */ #define zone_idx(zone) ((zone) - (zone)->zone_pgdat->node_zones) -/** - * for_each_pgdat - helper macro to iterate over all nodes - * @pgdat - pointer to a pg_data_t variable - * - * Meant to help with common loops of the form - * pgdat = pgdat_list; - * while(pgdat) { - * ... - * pgdat = pgdat->pgdat_next; - * } - */ -#define for_each_pgdat(pgdat) \ - for (pgdat = pgdat_list; pgdat; pgdat = pgdat->pgdat_next) - -/* - * next_zone - helper magic for for_each_zone() - * Thanks to William Lee Irwin III for this piece of ingenuity. - */ -static inline struct zone *next_zone(struct zone *zone) -{ - pg_data_t *pgdat = zone->zone_pgdat; - - if (zone < pgdat->node_zones + MAX_NR_ZONES - 1) - zone++; - else if (pgdat->pgdat_next) { - pgdat = pgdat->pgdat_next; - zone = pgdat->node_zones; - } else - zone = NULL; - - return zone; -} - -/** - * for_each_zone - helper macro to iterate over all memory zones - * @zone - pointer to struct zone variable - * - * The user only needs to declare the zone variable, for_each_zone - * fills it in. This basically means for_each_zone() is an - * easier to read version of this piece of code: - * - * for (pgdat = pgdat_list; pgdat; pgdat = pgdat->node_next) - * for (i = 0; i < MAX_NR_ZONES; ++i) { - * struct zone * z = pgdat->node_zones + i; - * ... - * } - * } - */ -#define for_each_zone(zone) \ - for (zone = pgdat_list->node_zones; zone; zone = next_zone(zone)) - static inline int populated_zone(struct zone *zone) { return (!!zone->present_pages); @@ -471,6 +421,62 @@ extern struct pglist_data contig_page_data; #endif /* !CONFIG_NEED_MULTIPLE_NODES */ +static inline struct pglist_data *first_online_pgdat(void) +{ + return NODE_DATA(first_online_node); +} + +static inline struct pglist_data *next_online_pgdat(struct pglist_data *pgdat) +{ + int nid = next_online_node(pgdat->node_id); + + if (nid == MAX_NUMNODES) + return NULL; + return NODE_DATA(nid); +} + + +/** + * for_each_pgdat - helper macro to iterate over all nodes + * @pgdat - pointer to a pg_data_t variable + */ +#define for_each_online_pgdat(pgdat) \ + for (pgdat = first_online_pgdat(); \ + pgdat; \ + pgdat = next_online_pgdat(pgdat)) + +/* + * next_zone - helper magic for for_each_zone() + * Thanks to William Lee Irwin III for this piece of ingenuity. + */ +static inline struct zone *next_zone(struct zone *zone) +{ + pg_data_t *pgdat = zone->zone_pgdat; + + if (zone < pgdat->node_zones + MAX_NR_ZONES - 1) + zone++; + else { + pgdat = next_online_pgdat(pgdat); + if (pgdat) + zone = pgdat->node_zones; + else + zone = NULL; + } + return zone; +} + +/** + * for_each_zone - helper macro to iterate over all memory zones + * @zone - pointer to struct zone variable + * + * The user only needs to declare the zone variable, for_each_zone + * fills it in. + */ +#define for_each_zone(zone) \ + for (zone = (first_online_pgdat())->node_zones; \ + zone; \ + zone = next_zone(zone)) + #ifdef CONFIG_SPARSEMEM #include #endif diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h index b959a4525cbd..1a9ef3e627d1 100644 --- a/include/linux/nodemask.h +++ b/include/linux/nodemask.h @@ -350,11 +350,15 @@ extern nodemask_t node_possible_map; #define num_possible_nodes() nodes_weight(node_possible_map) #define node_online(node) node_isset((node), node_online_map) #define node_possible(node) node_isset((node), node_possible_map) +#define first_online_node first_node(node_online_map) +#define next_online_node(nid) next_node((nid), node_online_map) #else #define num_online_nodes() 1 #define num_possible_nodes() 1 #define node_online(node) ((node) == 0) #define node_possible(node) ((node) == 0) +#define first_online_node 0 +#define next_online_node(nid) (MAX_NUMNODES) #endif #define any_online_node(mask) \ -- cgit v1.2.3 From 679bc9fbb508a0aac9539b2de747eb5849feb428 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:15:58 -0800 Subject: [PATCH] for_each_online_pgdat: for_each_bootmem Add a list_head to bootmem_data_t and make bootmems use it. bootmem list is sorted by node_boot_start. Only nodes against which init_bootmem() is called are linked to the list. (i386 allocates bootmem only from one node(0) not from all online nodes.) A summary: 1. for_each_online_pgdat() traverses all *online* nodes. 2. alloc_bootmem() allocates memory only from initialized-for-bootmem nodes. Signed-off-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 1 + mm/bootmem.c | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 7155452fb4a8..de3eb8d8ae26 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -38,6 +38,7 @@ typedef struct bootmem_data { unsigned long last_pos; unsigned long last_success; /* Previous allocation point. To speed * up searching */ + struct list_head list; } bootmem_data_t; extern unsigned long __init bootmem_bootmap_pages (unsigned long); diff --git a/mm/bootmem.c b/mm/bootmem.c index b55bd39fc5dd..d3e3bd2ffcea 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -33,6 +33,7 @@ EXPORT_SYMBOL(max_pfn); /* This is exported so * dma_get_required_mask(), which uses * it, can be an inline function */ +static LIST_HEAD(bdata_list); #ifdef CONFIG_CRASH_DUMP /* * If we have booted due to a crash, max_pfn will be a very low value. We need @@ -52,6 +53,27 @@ unsigned long __init bootmem_bootmap_pages (unsigned long pages) return mapsize; } +/* + * link bdata in order + */ +static void link_bootmem(bootmem_data_t *bdata) +{ + bootmem_data_t *ent; + if (list_empty(&bdata_list)) { + list_add(&bdata->list, &bdata_list); + return; + } + /* insert in order */ + list_for_each_entry(ent, &bdata_list, list) { + if (bdata->node_boot_start < ent->node_boot_start) { + list_add_tail(&bdata->list, &ent->list); + return; + } + } + list_add_tail(&bdata->list, &bdata_list); + return; +} + /* * Called once to set up the allocator itself. @@ -62,13 +84,11 @@ static unsigned long __init init_bootmem_core (pg_data_t *pgdat, bootmem_data_t *bdata = pgdat->bdata; unsigned long mapsize = ((end - start)+7)/8; - pgdat->pgdat_next = pgdat_list; - pgdat_list = pgdat; - mapsize = ALIGN(mapsize, sizeof(long)); bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT); bdata->node_boot_start = (start << PAGE_SHIFT); bdata->node_low_pfn = end; + link_bootmem(bdata); /* * Initially all pages are reserved - setup_arch() has to @@ -383,12 +403,11 @@ unsigned long __init free_all_bootmem (void) void * __init __alloc_bootmem(unsigned long size, unsigned long align, unsigned long goal) { - pg_data_t *pgdat = pgdat_list; + bootmem_data_t *bdata; void *ptr; - for_each_pgdat(pgdat) - if ((ptr = __alloc_bootmem_core(pgdat->bdata, size, - align, goal, 0))) + list_for_each_entry(bdata, &bdata_list, list) + if ((ptr = __alloc_bootmem_core(bdata, size, align, goal, 0))) return(ptr); /* @@ -416,11 +435,11 @@ void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, unsigne void * __init __alloc_bootmem_low(unsigned long size, unsigned long align, unsigned long goal) { - pg_data_t *pgdat = pgdat_list; + bootmem_data_t *bdata; void *ptr; - for_each_pgdat(pgdat) - if ((ptr = __alloc_bootmem_core(pgdat->bdata, size, + list_for_each_entry(bdata, &bdata_list, list) + if ((ptr = __alloc_bootmem_core(bdata, size, align, goal, LOW32LIMIT))) return(ptr); -- cgit v1.2.3 From ae0f15fb91274e67d78836d38c99ec363df33073 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:16:01 -0800 Subject: [PATCH] for_each_online_pgdat: remove pgdat_list By using for_each_online_pgdat(), pgdat_list is not necessary now. This patch removes it. Signed-off-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 3 --- mm/page_alloc.c | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 96eb08025092..0d12c3cf1f86 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -307,7 +307,6 @@ typedef struct pglist_data { unsigned long node_spanned_pages; /* total size of physical page range, including holes */ int node_id; - struct pglist_data *pgdat_next; wait_queue_head_t kswapd_wait; struct task_struct *kswapd; int kswapd_max_order; @@ -324,8 +323,6 @@ typedef struct pglist_data { #include -extern struct pglist_data *pgdat_list; - void __get_zone_counts(unsigned long *active, unsigned long *inactive, unsigned long *free, struct pglist_data *pgdat); void get_zone_counts(unsigned long *active, unsigned long *inactive, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ccc3713dd407..dc523a1f270d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -49,7 +49,6 @@ nodemask_t node_online_map __read_mostly = { { [0] = 1UL } }; EXPORT_SYMBOL(node_online_map); nodemask_t node_possible_map __read_mostly = NODE_MASK_ALL; EXPORT_SYMBOL(node_possible_map); -struct pglist_data *pgdat_list __read_mostly; unsigned long totalram_pages __read_mostly; unsigned long totalhigh_pages __read_mostly; long nr_swap_pages; @@ -2169,8 +2168,9 @@ static void *frag_start(struct seq_file *m, loff_t *pos) { pg_data_t *pgdat; loff_t node = *pos; - - for (pgdat = pgdat_list; pgdat && node; pgdat = pgdat->pgdat_next) + for (pgdat = first_online_pgdat(); + pgdat && node; + pgdat = next_online_pgdat(pgdat)) --node; return pgdat; @@ -2181,7 +2181,7 @@ static void *frag_next(struct seq_file *m, void *arg, loff_t *pos) pg_data_t *pgdat = (pg_data_t *)arg; (*pos)++; - return pgdat->pgdat_next; + return next_online_pgdat(pgdat); } static void frag_stop(struct seq_file *m, void *arg) -- cgit v1.2.3 From 95144c788dc01b6a0ff2c9c2222e37ffdab358b8 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Mon, 27 Mar 2006 01:16:02 -0800 Subject: [PATCH] uninline zone helpers Helper functions for for_each_online_pgdat/for_each_zone look too big to be inlined. Speed of these helper macro itself is not very important. (inner loops are tend to do more work than this) This patch make helper function to be out-of-lined. inline out-of-line .text 005c0680 005bf6a0 005c0680 - 005bf6a0 = FE0 = 4Kbytes. Signed-off-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 38 +++----------------------------------- mm/Makefile | 2 +- mm/mmzone.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 36 deletions(-) create mode 100644 mm/mmzone.c (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 0d12c3cf1f86..b5c21122c299 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -418,20 +418,9 @@ extern struct pglist_data contig_page_data; #endif /* !CONFIG_NEED_MULTIPLE_NODES */ -static inline struct pglist_data *first_online_pgdat(void) -{ - return NODE_DATA(first_online_node); -} - -static inline struct pglist_data *next_online_pgdat(struct pglist_data *pgdat) -{ - int nid = next_online_node(pgdat->node_id); - - if (nid == MAX_NUMNODES) - return NULL; - return NODE_DATA(nid); -} - +extern struct pglist_data *first_online_pgdat(void); +extern struct pglist_data *next_online_pgdat(struct pglist_data *pgdat); +extern struct zone *next_zone(struct zone *zone); /** * for_each_pgdat - helper macro to iterate over all nodes @@ -441,27 +430,6 @@ static inline struct pglist_data *next_online_pgdat(struct pglist_data *pgdat) for (pgdat = first_online_pgdat(); \ pgdat; \ pgdat = next_online_pgdat(pgdat)) - -/* - * next_zone - helper magic for for_each_zone() - * Thanks to William Lee Irwin III for this piece of ingenuity. - */ -static inline struct zone *next_zone(struct zone *zone) -{ - pg_data_t *pgdat = zone->zone_pgdat; - - if (zone < pgdat->node_zones + MAX_NR_ZONES - 1) - zone++; - else { - pgdat = next_online_pgdat(pgdat); - if (pgdat) - zone = pgdat->node_zones; - else - zone = NULL; - } - return zone; -} - /** * for_each_zone - helper macro to iterate over all memory zones * @zone - pointer to struct zone variable diff --git a/mm/Makefile b/mm/Makefile index f10c753dce6d..0b8f73f2ed16 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -10,7 +10,7 @@ mmu-$(CONFIG_MMU) := fremap.o highmem.o madvise.o memory.o mincore.o \ obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ page_alloc.o page-writeback.o pdflush.o \ readahead.o swap.o truncate.o vmscan.o \ - prio_tree.o util.o $(mmu-y) + prio_tree.o util.o mmzone.o $(mmu-y) obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o obj-$(CONFIG_HUGETLBFS) += hugetlb.o diff --git a/mm/mmzone.c b/mm/mmzone.c new file mode 100644 index 000000000000..b022370e612e --- /dev/null +++ b/mm/mmzone.c @@ -0,0 +1,50 @@ +/* + * linux/mm/mmzone.c + * + * management codes for pgdats and zones. + */ + + +#include +#include +#include +#include + +struct pglist_data *first_online_pgdat(void) +{ + return NODE_DATA(first_online_node); +} + +EXPORT_SYMBOL(first_online_pgdat); + +struct pglist_data *next_online_pgdat(struct pglist_data *pgdat) +{ + int nid = next_online_node(pgdat->node_id); + + if (nid == MAX_NUMNODES) + return NULL; + return NODE_DATA(nid); +} +EXPORT_SYMBOL(next_online_pgdat); + + +/* + * next_zone - helper magic for for_each_zone() + */ +struct zone *next_zone(struct zone *zone) +{ + pg_data_t *pgdat = zone->zone_pgdat; + + if (zone < pgdat->node_zones + MAX_NR_ZONES - 1) + zone++; + else { + pgdat = next_online_pgdat(pgdat); + if (pgdat) + zone = pgdat->node_zones; + else + zone = NULL; + } + return zone; +} +EXPORT_SYMBOL(next_zone); + -- cgit v1.2.3 From 22a9835c350782a5c3257343713932af3ac92ee0 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Mon, 27 Mar 2006 01:16:04 -0800 Subject: [PATCH] unify PFN_* macros Just about every architecture defines some macros to do operations on pfns. They're all virtually identical. This patch consolidates all of them. One minor glitch is that at least i386 uses them in a very skeletal header file. To keep away from #include dependency hell, I stuck the new definitions in a new, isolated header. Of all of the implementations, sh64 is the only one that varied by a bit. It used some masks to ensure that any sign-extension got ripped away before the arithmetic is done. This has been posted to that sh64 maintainers and the development list. Compiles on x86, x86_64, ia64 and ppc64. Signed-off-by: Dave Hansen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/kernel/setup.c | 9 +-------- arch/alpha/mm/numa.c | 4 +--- arch/arm26/mm/init.c | 7 +------ arch/cris/kernel/setup.c | 5 +---- arch/i386/kernel/setup.c | 1 + arch/i386/mm/discontig.c | 1 + arch/m32r/kernel/setup.c | 1 + arch/m32r/mm/discontig.c | 1 + arch/m32r/mm/init.c | 1 + arch/mips/ite-boards/ivr/init.c | 3 --- arch/mips/ite-boards/qed-4n-s01b/init.c | 3 --- arch/mips/kernel/setup.c | 9 +-------- arch/mips/mips-boards/generic/memory.c | 7 ++----- arch/mips/mips-boards/sim/sim_mem.c | 7 ++----- arch/mips/mm/init.c | 4 +--- arch/mips/sgi-ip27/ip27-memory.c | 3 +-- arch/sh/kernel/setup.c | 5 +---- arch/sh64/kernel/setup.c | 1 + arch/um/kernel/physmem.c | 3 +-- include/asm-i386/setup.h | 4 +--- include/asm-m32r/setup.h | 4 ---- include/asm-sh64/platform.h | 5 ----- include/linux/pfn.h | 9 +++++++++ 23 files changed, 29 insertions(+), 68 deletions(-) create mode 100644 include/linux/pfn.h (limited to 'include') diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index b4e5f8ff2b25..9402624453c2 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef CONFIG_MAGIC_SYSRQ #include #include @@ -241,9 +242,6 @@ reserve_std_resources(void) request_resource(io, standard_io_resources+i); } -#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) -#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) -#define PFN_PHYS(x) ((x) << PAGE_SHIFT) #define PFN_MAX PFN_DOWN(0x80000000) #define for_each_mem_cluster(memdesc, cluster, i) \ for ((cluster) = (memdesc)->cluster, (i) = 0; \ @@ -472,11 +470,6 @@ page_is_ram(unsigned long pfn) return 0; } -#undef PFN_UP -#undef PFN_DOWN -#undef PFN_PHYS -#undef PFN_MAX - void __init setup_arch(char **cmdline_p) { diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index 6d5251254f68..bf6b65c81bef 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -27,9 +28,6 @@ bootmem_data_t node_bdata[MAX_NUMNODES]; #define DBGDCONT(args...) #endif -#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) -#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) -#define PFN_PHYS(x) ((x) << PAGE_SHIFT) #define for_each_mem_cluster(memdesc, cluster, i) \ for ((cluster) = (memdesc)->cluster, (i) = 0; \ (i) < (memdesc)->numclusters; (i)++, (cluster)++) diff --git a/arch/arm26/mm/init.c b/arch/arm26/mm/init.c index e3ecaa453747..7da8a5205678 100644 --- a/arch/arm26/mm/init.c +++ b/arch/arm26/mm/init.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -101,12 +102,6 @@ struct node_info { int bootmap_pages; }; -#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) -#define PFN_UP(x) (PAGE_ALIGN(x) >> PAGE_SHIFT) -#define PFN_SIZE(x) ((x) >> PAGE_SHIFT) -#define PFN_RANGE(s,e) PFN_SIZE(PAGE_ALIGN((unsigned long)(e)) - \ - (((unsigned long)(s)) & PAGE_MASK)) - /* * FIXME: We really want to avoid allocating the bootmap bitmap * over the top of the initrd. Hopefully, this is located towards diff --git a/arch/cris/kernel/setup.c b/arch/cris/kernel/setup.c index 1ba57efff60d..619a6eefd893 100644 --- a/arch/cris/kernel/setup.c +++ b/arch/cris/kernel/setup.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -88,10 +89,6 @@ setup_arch(char **cmdline_p) init_mm.end_data = (unsigned long) &_edata; init_mm.brk = (unsigned long) &_end; -#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) -#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) -#define PFN_PHYS(x) ((x) << PAGE_SHIFT) - /* min_low_pfn points to the start of DRAM, start_pfn points * to the first DRAM pages after the kernel, and max_low_pfn * to the end of DRAM. diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 6917daa159ab..8c08660b4e5d 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -46,6 +46,7 @@ #include #include #include +#include #include