From f662cf7a74b4b47b977be838f6ed151456250780 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 12 Nov 2002 16:16:31 +0000 Subject: JFFS2 update. Various bugfixes -- deadlock in prepare_write() on extension of file fixed. -- corruption when reading a page where a multi-page hole ends fixed. -- oops on unlink of bad inodes fixed. -- allow bi-endian operation; mounting of non-host-endian file system is now possible. Optimisations -- switch to rbtrees for the inode fragment list. O(log n) insertion and lookup now. -- avoid checking all data crcs and building fragment trees at scan time. Do it later in GC. -- use 'point' method if available to use a pointer directly into the flash chip during scan, rather than always using memcpy into RAM first. -- start to track node 'pristine' status, for later use in GC optimisation -- we'll be able to copy those nodes intact without having to read them, decompress and recompress their payload, etc. Or indeed having to read_inode() their inode. -- fix ordering of work done from kupdated. We now erase a block, mark it free and stick it on the appropriate list, and go on to the next one. Before, we erased _all_ the pending blocks before marking any of them free, while everyone waited for us. --- include/linux/jffs2.h | 103 +++++++++++++++++++++++++++++--------------- include/linux/jffs2_fs_i.h | 5 ++- include/linux/jffs2_fs_sb.h | 6 ++- 3 files changed, 77 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 89ed92acf87a..958cab16d8a7 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -8,7 +8,7 @@ * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.24 2002/05/20 14:56:37 dwmw2 Exp $ + * $Id: jffs2.h,v 1.25 2002/08/20 21:37:27 dwmw2 Exp $ * */ @@ -68,30 +68,65 @@ compression type */ +/* These can go once we've made sure we've caught all uses without + byteswapping */ + +typedef struct { + uint32_t v32; +} __attribute__((packed)) jint32_t; + +typedef struct { + uint16_t v16; +} __attribute__((packed)) jint16_t; + +#define JFFS2_NATIVE_ENDIAN + +#if defined(JFFS2_NATIVE_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){x}) +#define cpu_to_je32(x) ((jint32_t){x}) + +#define je16_to_cpu(x) ((x).v16) +#define je32_to_cpu(x) ((x).v32) +#elif defined(JFFS2_BIG_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) +#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) + +#define je16_to_cpu(x) (be16_to_cpu(x.v16)) +#define je32_to_cpu(x) (be32_to_cpu(x.v32)) +#elif defined(JFFS2_LITTLE_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) +#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) + +#define je16_to_cpu(x) (le16_to_cpu(x.v16)) +#define je32_to_cpu(x) (le32_to_cpu(x.v32)) +#else +#error wibble +#endif + struct jffs2_unknown_node { /* All start like this */ - uint16_t magic; - uint16_t nodetype; - uint32_t totlen; /* So we can skip over nodes we don't grok */ - uint32_t hdr_crc; + jint16_t magic; + jint16_t nodetype; + jint32_t totlen; /* So we can skip over nodes we don't grok */ + jint32_t hdr_crc; } __attribute__((packed)); struct jffs2_raw_dirent { - uint16_t magic; - uint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ - uint32_t totlen; - uint32_t hdr_crc; - uint32_t pino; - uint32_t version; - uint32_t ino; /* == zero for unlink */ - uint32_t mctime; + jint16_t magic; + jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t pino; + jint32_t version; + jint32_t ino; /* == zero for unlink */ + jint32_t mctime; uint8_t nsize; uint8_t type; uint8_t unused[2]; - uint32_t node_crc; - uint32_t name_crc; + jint32_t node_crc; + jint32_t name_crc; uint8_t name[0]; } __attribute__((packed)); @@ -103,27 +138,27 @@ struct jffs2_raw_dirent */ struct jffs2_raw_inode { - uint16_t magic; /* A constant magic number. */ - uint16_t nodetype; /* == JFFS_NODETYPE_INODE */ - uint32_t totlen; /* Total length of this node (inc data, etc.) */ - uint32_t hdr_crc; - uint32_t ino; /* Inode number. */ - uint32_t version; /* Version number. */ - uint32_t mode; /* The file's type or mode. */ - uint16_t uid; /* The file's owner. */ - uint16_t gid; /* The file's group. */ - uint32_t isize; /* Total resultant size of this inode (used for truncations) */ - uint32_t atime; /* Last access time. */ - uint32_t mtime; /* Last modification time. */ - uint32_t ctime; /* Change time. */ - uint32_t offset; /* Where to begin to write. */ - uint32_t csize; /* (Compressed) data size */ - uint32_t dsize; /* Size of the node's data. (after decompression) */ + jint16_t magic; /* A constant magic number. */ + jint16_t nodetype; /* == JFFS_NODETYPE_INODE */ + jint32_t totlen; /* Total length of this node (inc data, etc.) */ + jint32_t hdr_crc; + jint32_t ino; /* Inode number. */ + jint32_t version; /* Version number. */ + jint32_t mode; /* The file's type or mode. */ + jint16_t uid; /* The file's owner. */ + jint16_t gid; /* The file's group. */ + jint32_t isize; /* Total resultant size of this inode (used for truncations) */ + jint32_t atime; /* Last access time. */ + jint32_t mtime; /* Last modification time. */ + jint32_t ctime; /* Change time. */ + jint32_t offset; /* Where to begin to write. */ + jint32_t csize; /* (Compressed) data size */ + jint32_t dsize; /* Size of the node's data. (after decompression) */ uint8_t compr; /* Compression algorithm used */ uint8_t usercompr; /* Compression algorithm requested by the user */ - uint16_t flags; /* See JFFS2_INO_FLAG_* */ - uint32_t data_crc; /* CRC for the (compressed) data. */ - uint32_t node_crc; /* CRC for the raw inode (excluding data) */ + jint16_t flags; /* See JFFS2_INO_FLAG_* */ + jint32_t data_crc; /* CRC for the (compressed) data. */ + jint32_t node_crc; /* CRC for the raw inode (excluding data) */ // uint8_t data[dsize]; } __attribute__((packed)); diff --git a/include/linux/jffs2_fs_i.h b/include/linux/jffs2_fs_i.h index 03530b6065d9..db91aa6c0322 100644 --- a/include/linux/jffs2_fs_i.h +++ b/include/linux/jffs2_fs_i.h @@ -1,9 +1,10 @@ -/* $Id: jffs2_fs_i.h,v 1.12 2002/03/06 13:59:21 dwmw2 Exp $ */ +/* $Id: jffs2_fs_i.h,v 1.15 2002/11/12 09:42:49 dwmw2 Exp $ */ #ifndef _JFFS2_FS_I #define _JFFS2_FS_I #include +#include struct jffs2_inode_info { /* We need an internal semaphore similar to inode->i_sem. @@ -18,7 +19,7 @@ struct jffs2_inode_info { uint32_t highest_version; /* List of data fragments which make up the file */ - struct jffs2_node_frag *fraglist; + struct rb_root fragtree; /* There may be one datanode which isn't referenced by any of the above fragments, if it contains a metadata update but no actual diff --git a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h index 611aa1b5b129..7cfabbd614cb 100644 --- a/include/linux/jffs2_fs_sb.h +++ b/include/linux/jffs2_fs_sb.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.32 2002/07/23 14:35:34 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.35 2002/11/12 09:42:18 dwmw2 Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -21,6 +21,8 @@ struct jffs2_sb_info { struct mtd_info *mtd; uint32_t highest_ino; + uint32_t checked_ino; + unsigned int flags; struct task_struct *gc_task; /* GC task struct */ @@ -38,10 +40,12 @@ struct jffs2_sb_info { uint32_t flash_size; uint32_t used_size; uint32_t dirty_size; + uint32_t wasted_size; uint32_t free_size; uint32_t erasing_size; uint32_t bad_size; uint32_t sector_size; + uint32_t unchecked_size; uint32_t nr_free_blocks; uint32_t nr_erasing_blocks; -- cgit v1.2.3 From fdd30fe27d07461d201a3ce41a2c49b1041451d9 Mon Sep 17 00:00:00 2001 From: James Morris Date: Thu, 14 Nov 2002 21:43:43 -0800 Subject: [CRYPTO] kstack cleanup (v0.28) --- crypto/api.c | 37 +++++++++++++++++++++++++++++++------ crypto/cipher.c | 30 +++++++++++++++++++----------- crypto/compress.c | 6 +++++- crypto/digest.c | 17 ++++++++++++----- crypto/hmac.c | 34 ++++++++++++++++++++++++++++------ crypto/internal.h | 23 ++++++++++++++++++++--- include/linux/crypto.h | 3 +++ 7 files changed, 118 insertions(+), 32 deletions(-) (limited to 'include/linux') diff --git a/crypto/api.c b/crypto/api.c index 833481f4e2c8..2db42767229d 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -74,19 +74,39 @@ static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags) return -EINVAL; } -static void crypto_init_ops(struct crypto_tfm *tfm) +static int crypto_init_ops(struct crypto_tfm *tfm) { switch (crypto_tfm_alg_type(tfm)) { case CRYPTO_ALG_TYPE_CIPHER: - crypto_init_cipher_ops(tfm); + return crypto_init_cipher_ops(tfm); + + case CRYPTO_ALG_TYPE_DIGEST: + return crypto_init_digest_ops(tfm); + + case CRYPTO_ALG_TYPE_COMP: + return crypto_init_compress_ops(tfm); + + default: + break; + } + + BUG(); + return -EINVAL; +} + +static void crypto_exit_ops(struct crypto_tfm *tfm) +{ + switch (crypto_tfm_alg_type(tfm)) { + case CRYPTO_ALG_TYPE_CIPHER: + crypto_exit_cipher_ops(tfm); break; case CRYPTO_ALG_TYPE_DIGEST: - crypto_init_digest_ops(tfm); + crypto_exit_digest_ops(tfm); break; case CRYPTO_ALG_TYPE_COMP: - crypto_init_compress_ops(tfm); + crypto_exit_compress_ops(tfm); break; default: @@ -110,6 +130,8 @@ struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags) memset(tfm, 0, sizeof(*tfm)); + memset(tfm, 0, sizeof(*tfm)); + if (alg->cra_ctxsize) { tfm->crt_ctx = kmalloc(alg->cra_ctxsize, GFP_KERNEL); if (tfm->crt_ctx == NULL) @@ -128,8 +150,11 @@ struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags) if (crypto_init_flags(tfm, flags)) goto out_free_work_block; - crypto_init_ops(tfm); - + if (crypto_init_ops(tfm)) { + crypto_exit_ops(tfm); + goto out_free_ctx; + } + goto out; out_free_work_block: diff --git a/crypto/cipher.c b/crypto/cipher.c index 87882e24fe41..04d27e10ed26 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -234,27 +234,19 @@ static int nocrypt(struct crypto_tfm *tfm, int crypto_init_cipher_flags(struct crypto_tfm *tfm, u32 flags) { - struct crypto_alg *alg = tfm->__crt_alg; u32 mode = flags & CRYPTO_TFM_MODE_MASK; tfm->crt_cipher.cit_mode = mode ? mode : CRYPTO_TFM_MODE_ECB; - - if (alg->cra_cipher.cia_ivsize && mode != CRYPTO_TFM_MODE_ECB) { - tfm->crt_cipher.cit_iv = - kmalloc(alg->cra_cipher.cia_ivsize, GFP_KERNEL); - if (tfm->crt_cipher.cit_iv == NULL) - return -ENOMEM; - } else - tfm->crt_cipher.cit_iv = NULL; - if (flags & CRYPTO_TFM_REQ_WEAK_KEY) tfm->crt_flags = CRYPTO_TFM_REQ_WEAK_KEY; return 0; } -void crypto_init_cipher_ops(struct crypto_tfm *tfm) +int crypto_init_cipher_ops(struct crypto_tfm *tfm) { + int ret = 0; + struct crypto_alg *alg = tfm->__crt_alg; struct cipher_tfm *ops = &tfm->crt_cipher; ops->cit_setkey = setkey; @@ -283,4 +275,20 @@ void crypto_init_cipher_ops(struct crypto_tfm *tfm) default: BUG(); } + + if (alg->cra_cipher.cia_ivsize && + ops->cit_mode != CRYPTO_TFM_MODE_ECB) { + + ops->cit_iv = kmalloc(alg->cra_cipher.cia_ivsize, GFP_KERNEL); + if (ops->cit_iv == NULL) + ret = -ENOMEM; + } + + return ret; +} + +void crypto_exit_cipher_ops(struct crypto_tfm *tfm) +{ + if (tfm->crt_cipher.cit_iv) + kfree(tfm->crt_cipher.cit_iv); } diff --git a/crypto/compress.c b/crypto/compress.c index f599a4e37954..773fc5764050 100644 --- a/crypto/compress.c +++ b/crypto/compress.c @@ -33,10 +33,14 @@ int crypto_init_compress_flags(struct crypto_tfm *tfm, u32 flags) return crypto_cipher_flags(flags) ? -EINVAL : 0; } -void crypto_init_compress_ops(struct crypto_tfm *tfm) +int crypto_init_compress_ops(struct crypto_tfm *tfm) { struct compress_tfm *ops = &tfm->crt_compress; ops->cot_compress = crypto_compress; ops->cot_decompress = crypto_decompress; + return 0; } + +void crypto_exit_compress_ops(struct crypto_tfm *tfm) +{ } diff --git a/crypto/digest.c b/crypto/digest.c index 83aae1d9eaf7..4db5f88ef38d 100644 --- a/crypto/digest.c +++ b/crypto/digest.c @@ -63,12 +63,19 @@ int crypto_init_digest_flags(struct crypto_tfm *tfm, u32 flags) return crypto_cipher_flags(flags) ? -EINVAL : 0; } -void crypto_init_digest_ops(struct crypto_tfm *tfm) +int crypto_init_digest_ops(struct crypto_tfm *tfm) { struct digest_tfm *ops = &tfm->crt_digest; - ops->dit_init = init; - ops->dit_update = update; - ops->dit_final = final; - ops->dit_digest = digest; + ops->dit_init = init; + ops->dit_update = update; + ops->dit_final = final; + ops->dit_digest = digest; + + return crypto_alloc_hmac_block(tfm); +} + +void crypto_exit_digest_ops(struct crypto_tfm *tfm) +{ + crypto_free_hmac_block(tfm); } diff --git a/crypto/hmac.c b/crypto/hmac.c index 5a70ad66bc9d..31cd7799c5d8 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "internal.h" @@ -31,18 +32,39 @@ static void hash_key(struct crypto_tfm *tfm, u8 *key, unsigned int keylen) } +int crypto_alloc_hmac_block(struct crypto_tfm *tfm) +{ + int ret = 0; + + BUG_ON(!crypto_tfm_alg_blocksize(tfm)); + + tfm->crt_digest.dit_hmac_block = kmalloc(crypto_tfm_alg_blocksize(tfm), + GFP_KERNEL); + if (tfm->crt_digest.dit_hmac_block == NULL) + ret = -ENOMEM; + + return ret; + +} + +void crypto_free_hmac_block(struct crypto_tfm *tfm) +{ + if (tfm->crt_digest.dit_hmac_block) + kfree(tfm->crt_digest.dit_hmac_block); +} + void crypto_hmac_init(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen) { unsigned int i; struct scatterlist tmp; - char *ipad = tfm->crt_work_block; - + char *ipad = tfm->crt_digest.dit_hmac_block; + if (*keylen > crypto_tfm_alg_blocksize(tfm)) { hash_key(tfm, key, *keylen); *keylen = crypto_tfm_alg_digestsize(tfm); } - memset(ipad, 0, crypto_tfm_alg_blocksize(tfm) + 1); + memset(ipad, 0, crypto_tfm_alg_blocksize(tfm)); memcpy(ipad, key, *keylen); for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++) @@ -67,8 +89,8 @@ void crypto_hmac_final(struct crypto_tfm *tfm, u8 *key, { unsigned int i; struct scatterlist tmp; - char *opad = tfm->crt_work_block; - + char *opad = tfm->crt_digest.dit_hmac_block; + if (*keylen > crypto_tfm_alg_blocksize(tfm)) { hash_key(tfm, key, *keylen); *keylen = crypto_tfm_alg_digestsize(tfm); @@ -76,7 +98,7 @@ void crypto_hmac_final(struct crypto_tfm *tfm, u8 *key, crypto_digest_final(tfm, out); - memset(opad, 0, crypto_tfm_alg_blocksize(tfm) + 1); + memset(opad, 0, crypto_tfm_alg_blocksize(tfm)); memcpy(opad, key, *keylen); for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++) diff --git a/crypto/internal.h b/crypto/internal.h index a75326211f1e..408bc1bb5b0c 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -52,13 +52,30 @@ static inline struct crypto_alg *crypto_alg_mod_lookup(const char *name) } #endif +#ifdef CONFIG_CRYPTO_HMAC +int crypto_alloc_hmac_block(struct crypto_tfm *tfm); +void crypto_free_hmac_block(struct crypto_tfm *tfm); +#else +static inline int crypto_alloc_hmac_block(struct crypto_tfm *tfm) +{ + return 0; +} + +static inline void crypto_free_hmac_block(struct crypto_tfm *tfm) +{ } +#endif + int crypto_init_digest_flags(struct crypto_tfm *tfm, u32 flags); int crypto_init_cipher_flags(struct crypto_tfm *tfm, u32 flags); int crypto_init_compress_flags(struct crypto_tfm *tfm, u32 flags); -void crypto_init_digest_ops(struct crypto_tfm *tfm); -void crypto_init_cipher_ops(struct crypto_tfm *tfm); -void crypto_init_compress_ops(struct crypto_tfm *tfm); +int crypto_init_digest_ops(struct crypto_tfm *tfm); +int crypto_init_cipher_ops(struct crypto_tfm *tfm); +int crypto_init_compress_ops(struct crypto_tfm *tfm); + +void crypto_exit_digest_ops(struct crypto_tfm *tfm); +void crypto_exit_cipher_ops(struct crypto_tfm *tfm); +void crypto_exit_compress_ops(struct crypto_tfm *tfm); #endif /* _CRYPTO_INTERNAL_H */ diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 7fab3c0435ac..35e3a20a2f5a 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -144,6 +144,9 @@ struct digest_tfm { void (*dit_final)(struct crypto_tfm *tfm, u8 *out); void (*dit_digest)(struct crypto_tfm *tfm, struct scatterlist *sg, unsigned int nsg, u8 *out); +#ifdef CONFIG_CRYPTO_HMAC + void *dit_hmac_block; +#endif }; struct compress_tfm { -- cgit v1.2.3 From 3a70869407b8798fb6170fdcaae5ee90d3477478 Mon Sep 17 00:00:00 2001 From: Maneesh Soni Date: Sun, 17 Nov 2002 16:34:50 -0800 Subject: [PATCH] dcache usage cleanups This cleans up the dcache code to always use the proper dcache functions (d_unhashed and __d_drop) instead of accessing the dentry lists directly. In other words: use "d_unhashed(dentry)" instead of doing a manual "list_empty(&dentry->d_hash)" test. And use "__d_drop(dentry)" instead of doing "list_del_init(&dentry->d_hash)" by hand. This will help the dcache-rcu patches. --- drivers/usb/core/inode.c | 2 +- fs/autofs4/root.c | 2 +- fs/dcache.c | 23 +++++++++++------------ fs/exec.c | 4 ++-- fs/intermezzo/journal.c | 8 ++++---- fs/libfs.c | 4 ++-- fs/namei.c | 2 +- fs/nfs/dir.c | 2 +- include/linux/dcache.h | 7 ++++++- kernel/exit.c | 4 ++-- 10 files changed, 31 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 9353bf39c417..a7306cf26a35 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -255,7 +255,7 @@ static void d_unhash(struct dentry *dentry) if (atomic_read(&dentry->d_count) != 2) break; case 2: - list_del_init(&dentry->d_hash); + __d_drop(dentry); } spin_unlock(&dcache_lock); } diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 896befabf494..0b60f7b0e411 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -418,7 +418,7 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) unlock_kernel(); return -ENOTEMPTY; } - list_del_init(&dentry->d_hash); + __d_drop(dentry); spin_unlock(&dcache_lock); dput(ino->dentry); diff --git a/fs/dcache.c b/fs/dcache.c index 01240a36688a..931b475e0186 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -137,7 +137,7 @@ repeat: goto unhash_it; } /* Unreachable? Get rid of it */ - if (list_empty(&dentry->d_hash)) + if (d_unhashed(dentry)) goto kill_it; list_add(&dentry->d_lru, &dentry_unused); dentry_stat.nr_unused++; @@ -146,7 +146,7 @@ repeat: return; unhash_it: - list_del_init(&dentry->d_hash); + __d_drop(dentry); kill_it: { struct dentry *parent; @@ -181,7 +181,7 @@ int d_invalidate(struct dentry * dentry) * If it's already been dropped, return OK. */ spin_lock(&dcache_lock); - if (list_empty(&dentry->d_hash)) { + if (d_unhashed(dentry)) { spin_unlock(&dcache_lock); return 0; } @@ -212,7 +212,7 @@ int d_invalidate(struct dentry * dentry) } } - list_del_init(&dentry->d_hash); + __d_drop(dentry); spin_unlock(&dcache_lock); return 0; } @@ -259,7 +259,7 @@ struct dentry * d_find_alias(struct inode *inode) tmp = next; next = tmp->next; alias = list_entry(tmp, struct dentry, d_alias); - if (!list_empty(&alias->d_hash)) { + if (!d_unhashed(alias)) { if (alias->d_flags & DCACHE_DISCONNECTED) discon_alias = alias; else { @@ -308,7 +308,7 @@ static inline void prune_one_dentry(struct dentry * dentry) { struct dentry * parent; - list_del_init(&dentry->d_hash); + __d_drop(dentry); list_del(&dentry->d_child); dentry_stat.nr_dentry--; /* For d_free, below */ dentry_iput(dentry); @@ -997,7 +997,7 @@ void d_delete(struct dentry * dentry) void d_rehash(struct dentry * entry) { struct list_head *list = d_hash(entry->d_parent, entry->d_name.hash); - if (!list_empty(&entry->d_hash)) BUG(); + if (!d_unhashed(entry)) BUG(); spin_lock(&dcache_lock); list_add(&entry->d_hash, list); spin_unlock(&dcache_lock); @@ -1065,11 +1065,10 @@ void d_move(struct dentry * dentry, struct dentry * target) spin_lock(&dcache_lock); /* Move the dentry to the target hash queue */ - list_del(&dentry->d_hash); - list_add(&dentry->d_hash, &target->d_hash); + list_move(&dentry->d_hash, &target->d_hash); /* Unhash the target: dput() will then get rid of it */ - list_del_init(&target->d_hash); + __d_drop(target); list_del(&dentry->d_child); list_del(&target->d_child); @@ -1121,7 +1120,7 @@ static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt, *--end = '\0'; buflen--; - if (!IS_ROOT(dentry) && list_empty(&dentry->d_hash)) { + if (!IS_ROOT(dentry) && d_unhashed(dentry)) { buflen -= 10; end -= 10; memcpy(end, " (deleted)", 10); @@ -1223,7 +1222,7 @@ asmlinkage long sys_getcwd(char *buf, unsigned long size) error = -ENOENT; /* Has the current directory has been unlinked? */ spin_lock(&dcache_lock); - if (pwd->d_parent == pwd || !list_empty(&pwd->d_hash)) { + if (pwd->d_parent == pwd || !d_unhashed(pwd)) { unsigned long len; char * cwd; diff --git a/fs/exec.c b/fs/exec.c index 837c3bc24d61..a889af64d09e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -524,9 +524,9 @@ static struct dentry *clean_proc_dentry(struct task_struct *p) if (proc_dentry) { spin_lock(&dcache_lock); - if (!list_empty(&proc_dentry->d_hash)) { + if (!d_unhashed(proc_dentry)) { dget_locked(proc_dentry); - list_del_init(&proc_dentry->d_hash); + __d_drop(proc_dentry); } else proc_dentry = NULL; spin_unlock(&dcache_lock); diff --git a/fs/intermezzo/journal.c b/fs/intermezzo/journal.c index 655fe5f7a502..8d64c8fec746 100644 --- a/fs/intermezzo/journal.c +++ b/fs/intermezzo/journal.c @@ -261,7 +261,7 @@ char * presto_path(struct dentry *dentry, struct dentry *root, *--end = '\0'; buflen--; - if (dentry->d_parent != dentry && list_empty(&dentry->d_hash)) { + if (dentry->d_parent != dentry && d_unhashed(dentry)) { buflen -= 10; end -= 10; memcpy(end, " (deleted)", 10); @@ -1518,7 +1518,7 @@ int presto_journal_setattr(struct rec_info *rec, struct presto_file_set *fset, } if (!dentry->d_inode || (dentry->d_inode->i_nlink == 0) - || ((dentry->d_parent != dentry) && list_empty(&dentry->d_hash))) { + || ((dentry->d_parent != dentry) && d_unhashed(dentry))) { EXIT; return 0; } @@ -2129,7 +2129,7 @@ presto_journal_close(struct rec_info *rec, struct presto_file_set *fset, } if (!dentry->d_inode || (dentry->d_inode->i_nlink == 0) - || ((dentry->d_parent != dentry) && list_empty(&dentry->d_hash))) { + || ((dentry->d_parent != dentry) && d_unhashed(dentry))) { EXIT; return 0; } @@ -2391,7 +2391,7 @@ int presto_journal_set_ext_attr (struct rec_info *rec, } if (!dentry->d_inode || (dentry->d_inode->i_nlink == 0) - || ((dentry->d_parent != dentry) && list_empty(&dentry->d_hash))) { + || ((dentry->d_parent != dentry) && d_unhashed(dentry))) { EXIT; return 0; } diff --git a/fs/libfs.c b/fs/libfs.c index ea1ec8d09310..07ab22e1c256 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -70,7 +70,7 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) while (n && p != &file->f_dentry->d_subdirs) { struct dentry *next; next = list_entry(p, struct dentry, d_child); - if (!list_empty(&next->d_hash) && next->d_inode) + if (!d_unhashed(next) && next->d_inode) n--; p = p->next; } @@ -127,7 +127,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) for (p=q->next; p != &dentry->d_subdirs; p=p->next) { struct dentry *next; next = list_entry(p, struct dentry, d_child); - if (list_empty(&next->d_hash) || !next->d_inode) + if (d_unhashed(next) || !next->d_inode) continue; spin_unlock(&dcache_lock); diff --git a/fs/namei.c b/fs/namei.c index 55f9685b7c33..b2dc8dffd6ca 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1551,7 +1551,7 @@ static void d_unhash(struct dentry *dentry) if (atomic_read(&dentry->d_count) != 2) break; case 2: - list_del_init(&dentry->d_hash); + __d_drop(dentry); } spin_unlock(&dcache_lock); } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 22a1896f2290..8f7f90ba5f10 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1001,7 +1001,7 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) return error; } if (!d_unhashed(dentry)) { - list_del_init(&dentry->d_hash); + __d_drop(dentry); need_rehash = 1; } spin_unlock(&dcache_lock); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 3a80e6e7beb7..535ca54d1d37 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -160,10 +160,15 @@ extern rwlock_t dparent_lock; * timeouts or autofs deletes). */ +static __inline__ void __d_drop(struct dentry * dentry) +{ + list_del_init(&dentry->d_hash); +} + static __inline__ void d_drop(struct dentry * dentry) { spin_lock(&dcache_lock); - list_del_init(&dentry->d_hash); + __d_drop(dentry); spin_unlock(&dcache_lock); } diff --git a/kernel/exit.c b/kernel/exit.c index 038da036771b..b923fd64591b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -47,9 +47,9 @@ static struct dentry * __unhash_process(struct task_struct *p) proc_dentry = p->proc_dentry; if (unlikely(proc_dentry != NULL)) { spin_lock(&dcache_lock); - if (!list_empty(&proc_dentry->d_hash)) { + if (!d_unhashed(proc_dentry)) { dget_locked(proc_dentry); - list_del_init(&proc_dentry->d_hash); + __d_drop(proc_dentry); } else proc_dentry = NULL; spin_unlock(&dcache_lock); -- cgit v1.2.3 From bcfe6cb3fe286ec0357722a8fe02e2e4d86700e6 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Sun, 17 Nov 2002 21:38:59 -0800 Subject: [PATCH] s390: module loader. --- arch/s390/kernel/Makefile | 2 +- arch/s390/kernel/entry.S | 6 +- arch/s390/kernel/module.c | 174 ++++++++++++++++++++++++++++++++++++++ arch/s390/kernel/traps.c | 8 +- arch/s390/mm/extable.c | 14 ++-- arch/s390x/kernel/Makefile | 2 +- arch/s390x/kernel/entry.S | 6 +- arch/s390x/kernel/linux32.c | 115 ------------------------- arch/s390x/kernel/module.c | 190 ++++++++++++++++++++++++++++++++++++++++++ arch/s390x/kernel/traps.c | 8 +- arch/s390x/kernel/wrapper32.S | 20 ----- arch/s390x/mm/extable.c | 23 ++--- include/asm-s390/module.h | 21 ++++- include/asm-s390x/module.h | 21 ++++- include/linux/elf.h | 31 +++++++ 15 files changed, 465 insertions(+), 176 deletions(-) create mode 100644 arch/s390/kernel/module.c create mode 100644 arch/s390x/kernel/module.c (limited to 'include/linux') diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 490166a3f0f7..f771591a94c5 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -10,7 +10,7 @@ obj-y := entry.o bitmap.o traps.o time.o process.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ semaphore.o reipl.o s390_ext.o debug.o -obj-$(CONFIG_MODULES) += s390_ksyms.o +obj-$(CONFIG_MODULES) += s390_ksyms.o module.o obj-$(CONFIG_SMP) += smp.o # diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index c6670485bc40..8c205abed224 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -474,10 +474,10 @@ sys_call_table: .long sys_adjtimex .long sys_mprotect /* 125 */ .long sys_sigprocmask - .long sys_create_module + .long sys_ni_syscall /* old "create module" */ .long sys_init_module .long sys_delete_module - .long sys_get_kernel_syms /* 130 */ + .long sys_ni_syscall /* 130: old get_kernel_syms */ .long sys_quotactl .long sys_getpgid .long sys_fchdir @@ -514,7 +514,7 @@ sys_call_table: .long sys_setresuid16 .long sys_getresuid16 /* 165 */ .long sys_ni_syscall /* for vm86 */ - .long sys_query_module + .long sys_ni_syscall /* old sys_query_module */ .long sys_poll .long sys_nfsservctl .long sys_setresgid16 /* 170 */ diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c new file mode 100644 index 000000000000..314160ffb68f --- /dev/null +++ b/arch/s390/kernel/module.c @@ -0,0 +1,174 @@ +/* + * arch/s390x/kernel/module.c - Kernel module help for s390x. + * + * S390 version + * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Arnd Bergmann (arndb@de.ibm.com) + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * based on i386 version + * Copyright (C) 2001 Rusty Russell. + * + * 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 +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt , ...) +#endif + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +/* s390/s390x needs additional memory for GOT/PLT sections. */ +long module_core_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + // FIXME: add space needed for GOT/PLT + return module->core_size; +} + +long module_init_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return module->init_size; +} + + + +int apply_relocate(Elf_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + ElfW(Rel) *rel = (void *)sechdrs[relsec].sh_offset; + ElfW(Sym) *sym; + ElfW(Addr) *location; + + DEBUGP("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset + + rel[i].r_offset; + /* This is the symbol it is referring to */ + sym = (ElfW(Sym) *)sechdrs[symindex].sh_offset + + ELFW(R_SYM)(rel[i].r_info); + if (!sym->st_value) { + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + + switch (ELF_R_TYPE(rel[i].r_info)) { + case R_390_8: /* Direct 8 bit. */ + *(u8*) location += sym->st_value; + break; + case R_390_12: /* Direct 12 bit. */ + *(u16*) location = (*(u16*) location & 0xf000) | + (sym->st_value & 0xfff); + break; + case R_390_16: /* Direct 16 bit. */ + *(u16*) location += sym->st_value; + break; + case R_390_32: /* Direct 32 bit. */ + *(u32*) location += sym->st_value; + break; + case R_390_PC16: /* PC relative 16 bit. */ + *(u16*) location += sym->st_value + - (unsigned long )location; + + case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ + *(u16*) location += (sym->st_value + - (unsigned long )location) >> 1; + case R_390_PC32: /* PC relative 32 bit. */ + *(u32*) location += sym->st_value + - (unsigned long )location; + break; + case R_390_GOT12: /* 12 bit GOT offset. */ + case R_390_GOT16: /* 16 bit GOT offset. */ + case R_390_GOT32: /* 32 bit GOT offset. */ + // FIXME: TODO + break; + + case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */ + case R_390_PLT32: /* 32 bit PC relative PLT address. */ + // FIXME: TODO + break; + case R_390_GLOB_DAT: /* Create GOT entry. */ + case R_390_JMP_SLOT: /* Create PLT entry. */ + *location = sym->st_value; + break; + case R_390_RELATIVE: /* Adjust by program base. */ + // FIXME: TODO + break; + case R_390_GOTOFF: /* 32 bit offset to GOT. */ + // FIXME: TODO + break; + case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */ + // FIXME: TODO + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %lu\n", + me->name, + (unsigned long)ELF_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index da15086acc86..5eb5923e001b 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -72,8 +72,8 @@ extern char _stext, _etext; #ifdef CONFIG_MODULES -extern struct module *module_list; -extern struct module kernel_module; +/* FIXME: Accessed without a lock --RR */ +extern struct list_head modules; static inline int kernel_text_address(unsigned long addr) { @@ -84,11 +84,11 @@ static inline int kernel_text_address(unsigned long addr) addr <= (unsigned long) &_etext) return 1; - for (mod = module_list; mod != &kernel_module; mod = mod->next) { + list_for_each_entry(mod, &modules, list) { /* mod_bound tests for addr being inside the vmalloc'ed * module area. Of course it'd be better to test only * for the .text subset... */ - if (mod_bound(addr, 0, mod)) { + if (mod_bound((void*)addr, 0, mod)) { retval = 1; break; } diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c index 1ba564ee3d31..d408152fd9af 100644 --- a/arch/s390/mm/extable.c +++ b/arch/s390/mm/extable.c @@ -42,6 +42,7 @@ extern spinlock_t modlist_lock; unsigned long search_exception_table(unsigned long addr) { + struct list_head *i; unsigned long ret = 0; #ifndef CONFIG_MODULES @@ -52,16 +53,17 @@ search_exception_table(unsigned long addr) return ret; #else unsigned long flags; - /* The kernel is the last "module" -- no need to treat it special. */ - struct module *mp; addr &= 0x7fffffff; /* remove amode bit from address */ + /* The kernel is the last "module" -- no need to treat it special. */ spin_lock_irqsave(&modlist_lock, flags); - for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING))) + list_for_each(i, &extables) { + struct exception_table *ex + = list_entry(i, struct exception_table, list); + if (ex->num_entries == 0) continue; - ret = search_one_table(mp->ex_table_start, - mp->ex_table_end - 1, addr); + ret = search_one_table(ex->entry, + ex->entry + ex->num_entries - 1, addr); if (ret) { ret = ret | PSW_ADDR_AMODE31; break; diff --git a/arch/s390x/kernel/Makefile b/arch/s390x/kernel/Makefile index 2da118443689..2a8f31e51909 100644 --- a/arch/s390x/kernel/Makefile +++ b/arch/s390x/kernel/Makefile @@ -12,7 +12,7 @@ obj-y := entry.o bitmap.o traps.o time.o process.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ semaphore.o reipl.o s390_ext.o debug.o -obj-$(CONFIG_MODULES) += s390_ksyms.o +obj-$(CONFIG_MODULES) += s390_ksyms.o module.o obj-$(CONFIG_SMP) += smp.o # diff --git a/arch/s390x/kernel/entry.S b/arch/s390x/kernel/entry.S index 07df5b3dcb54..ad8287d24467 100644 --- a/arch/s390x/kernel/entry.S +++ b/arch/s390x/kernel/entry.S @@ -503,10 +503,10 @@ sys_call_table: .long SYSCALL(sys_adjtimex,sys32_adjtimex_wrapper) .long SYSCALL(sys_mprotect,sys32_mprotect_wrapper) /* 125 */ .long SYSCALL(sys_sigprocmask,sys32_sigprocmask_wrapper) - .long SYSCALL(sys_create_module,sys32_create_module_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old "create module" */ .long SYSCALL(sys_init_module,sys32_init_module_wrapper) .long SYSCALL(sys_delete_module,sys32_delete_module_wrapper) - .long SYSCALL(sys_get_kernel_syms,sys32_get_kernel_syms_wrapper) /* 130 */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 130: old get_kernel_syms */ .long SYSCALL(sys_quotactl,sys32_quotactl_wrapper) .long SYSCALL(sys_getpgid,sys32_getpgid_wrapper) .long SYSCALL(sys_fchdir,sys32_fchdir_wrapper) @@ -543,7 +543,7 @@ sys_call_table: .long SYSCALL(sys_ni_syscall,sys32_setresuid16_wrapper) /* old setresuid16 syscall */ .long SYSCALL(sys_ni_syscall,sys32_getresuid16_wrapper) /* old getresuid16 syscall */ .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* for vm86 */ - .long SYSCALL(sys_query_module,sys32_query_module_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old sys_query_module */ .long SYSCALL(sys_poll,sys32_poll_wrapper) .long SYSCALL(sys_nfsservctl,sys32_nfsservctl_wrapper) .long SYSCALL(sys_ni_syscall,sys32_setresgid16_wrapper) /* old setresgid16 syscall */ diff --git a/arch/s390x/kernel/linux32.c b/arch/s390x/kernel/linux32.c index 8490920b29b3..3a5579b74110 100644 --- a/arch/s390x/kernel/linux32.c +++ b/arch/s390x/kernel/linux32.c @@ -3136,13 +3136,6 @@ out: #ifdef CONFIG_MODULES -extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size); - -asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size) -{ - return sys_create_module(name_user, (size_t)size); -} - extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user); /* Hey, when you're trying to init module, take time and prepare us a nice 64bit @@ -3421,103 +3414,13 @@ qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) return error; } -asmlinkage int sys32_query_module(char *name_user, int which, char *buf, __kernel_size_t32 bufsize, u32 ret) -{ - struct module *mod; - int err; - - lock_kernel(); - if (name_user == 0) { - /* This finds "kernel_module" which is not exported. */ - for(mod = module_list; mod->next != NULL; mod = mod->next) - ; - } else { - long namelen; - char *name; - - if ((namelen = get_mod_name(name_user, &name)) < 0) { - err = namelen; - goto out; - } - err = -ENOENT; - if (namelen == 0) { - /* This finds "kernel_module" which is not exported. */ - for(mod = module_list; mod->next != NULL; mod = mod->next) - ; - } else if ((mod = find_module(name)) == NULL) { - put_mod_name(name); - goto out; - } - put_mod_name(name); - } - - switch (which) - { - case 0: - err = 0; - break; - case QM_MODULES: - err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - case QM_DEPS: - err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - case QM_REFS: - err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - case QM_SYMBOLS: - err = qm_symbols(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - case QM_INFO: - err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - default: - err = -EINVAL; - break; - } -out: - unlock_kernel(); - return err; -} - struct kernel_sym32 { u32 value; char name[60]; }; - -extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table); - -asmlinkage int sys32_get_kernel_syms(struct kernel_sym32 *table) -{ - int len, i; - struct kernel_sym *tbl; - mm_segment_t old_fs; - - len = sys_get_kernel_syms(NULL); - if (!table) return len; - tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); - if (!tbl) return -ENOMEM; - old_fs = get_fs(); - set_fs (KERNEL_DS); - sys_get_kernel_syms(tbl); - set_fs (old_fs); - for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) { - if (put_user (tbl[i].value, &table->value) || - copy_to_user (table->name, tbl[i].name, 60)) - break; - } - kfree (tbl); - return i; -} #else /* CONFIG_MODULES */ -asmlinkage unsigned long -sys32_create_module(const char *name_user, size_t size) -{ - return -ENOSYS; -} - asmlinkage int sys32_init_module(const char *name_user, struct module *mod_user) { @@ -3530,24 +3433,6 @@ sys32_delete_module(const char *name_user) return -ENOSYS; } -asmlinkage int -sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize, - size_t *ret) -{ - /* Let the program know about the new interface. Not that - it'll do them much good. */ - if (which == 0) - return 0; - - return -ENOSYS; -} - -asmlinkage int -sys32_get_kernel_syms(struct kernel_sym *table) -{ - return -ENOSYS; -} - #endif /* CONFIG_MODULES */ /* Stuff for NFS server syscalls... */ diff --git a/arch/s390x/kernel/module.c b/arch/s390x/kernel/module.c new file mode 100644 index 000000000000..28b0627abc7f --- /dev/null +++ b/arch/s390x/kernel/module.c @@ -0,0 +1,190 @@ +/* + * arch/s390x/kernel/module.c - Kernel module help for s390x. + * + * S390 version + * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Arnd Bergmann (arndb@de.ibm.com) + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * based on i386 version + * Copyright (C) 2001 Rusty Russell. + * + * 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 +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt , ...) +#endif + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +/* s390/s390x needs additional memory for GOT/PLT sections. */ +long module_core_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + // FIXME: add space needed for GOT/PLT + return module->core_size; +} + +long module_init_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return module->init_size; +} + + + +int apply_relocate(Elf_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + ElfW(Rel) *rel = (void *)sechdrs[relsec].sh_offset; + ElfW(Sym) *sym; + ElfW(Addr) *location; + + DEBUGP("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset + + rel[i].r_offset; + /* This is the symbol it is referring to */ + sym = (ElfW(Sym) *)sechdrs[symindex].sh_offset + + ELFW(R_SYM)(rel[i].r_info); + if (!sym->st_value) { + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + + switch (ELF_R_TYPE(rel[i].r_info)) { + case R_390_8: /* Direct 8 bit. */ + *(u8*) location += sym->st_value; + break; + case R_390_12: /* Direct 12 bit. */ + *(u16*) location = (*(u16*) location & 0xf000) | + (sym->st_value & 0xfff); + break; + case R_390_16: /* Direct 16 bit. */ + *(u16*) location += sym->st_value; + break; + case R_390_32: /* Direct 32 bit. */ + *(u32*) location += sym->st_value; + break; + case R_390_64: /* Direct 64 bit. */ + *(u64*) location += sym->st_value; + break; + case R_390_PC16: /* PC relative 16 bit. */ + *(u16*) location += sym->st_value + - (unsigned long )location; + + case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ + *(u16*) location += (sym->st_value + - (unsigned long )location) >> 1; + case R_390_PC32: /* PC relative 32 bit. */ + *(u32*) location += sym->st_value + - (unsigned long )location; + break; + case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */ + *(u32*) location += (sym->st_value + - (unsigned long )location) >> 1; + break; + case R_390_PC64: /* PC relative 64 bit. */ + *(u64*) location += sym->st_value + - (unsigned long )location; + break; + case R_390_GOT12: /* 12 bit GOT offset. */ + case R_390_GOT16: /* 16 bit GOT offset. */ + case R_390_GOT32: /* 32 bit GOT offset. */ + case R_390_GOT64: /* 64 bit GOT offset. */ + case R_390_GOTENT: /* 32 bit PC rel. to GOT entry >> 1. */ + // FIXME: TODO + break; + + case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */ + case R_390_PLT32: /* 32 bit PC relative PLT address. */ + case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */ + case R_390_PLT64: /* 64 bit PC relative PLT address. */ + // FIXME: TODO + break; + case R_390_GLOB_DAT: /* Create GOT entry. */ + case R_390_JMP_SLOT: /* Create PLT entry. */ + *location = sym->st_value; + break; + case R_390_RELATIVE: /* Adjust by program base. */ + // FIXME: TODO + break; + case R_390_GOTOFF: /* 32 bit offset to GOT. */ + // FIXME: TODO + break; + case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */ + case R_390_GOTPCDBL: /* 32 bit PC rel. GOT shifted by 1. */ + // FIXME: TODO + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %lu\n", + me->name, + (unsigned long)ELF_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} diff --git a/arch/s390x/kernel/traps.c b/arch/s390x/kernel/traps.c index 7be25061bc0b..e67855f55ac4 100644 --- a/arch/s390x/kernel/traps.c +++ b/arch/s390x/kernel/traps.c @@ -74,8 +74,8 @@ extern char _stext, _etext; #ifdef CONFIG_MODULES -extern struct module *module_list; -extern struct module kernel_module; +/* FIXME: Accessed without a lock --RR */ +extern struct list_head modules; static inline int kernel_text_address(unsigned long addr) { @@ -86,11 +86,11 @@ static inline int kernel_text_address(unsigned long addr) addr <= (unsigned long) &_etext) return 1; - for (mod = module_list; mod != &kernel_module; mod = mod->next) { + list_for_each_entry(mod, &modules, list) { /* mod_bound tests for addr being inside the vmalloc'ed * module area. Of course it'd be better to test only * for the .text subset... */ - if (mod_bound(addr, 0, mod)) { + if (mod_bound((void*)addr, 0, mod)) { retval = 1; break; } diff --git a/arch/s390x/kernel/wrapper32.S b/arch/s390x/kernel/wrapper32.S index 131495785573..b4571f2a26d0 100644 --- a/arch/s390x/kernel/wrapper32.S +++ b/arch/s390x/kernel/wrapper32.S @@ -564,12 +564,6 @@ sys32_sigprocmask_wrapper: llgtr %r4,%r4 # old_sigset_emu31 * jg sys32_sigprocmask # branch to system call - .globl sys32_create_module_wrapper -sys32_create_module_wrapper: - llgtr %r2,%r2 # const char * - llgfr %r3,%r3 # size_t - jg sys32_create_module # branch to system call - .globl sys32_init_module_wrapper sys32_init_module_wrapper: llgtr %r2,%r2 # const char * @@ -581,11 +575,6 @@ sys32_delete_module_wrapper: llgtr %r2,%r2 # const char * jg sys32_delete_module # branch to system call - .globl sys32_get_kernel_syms_wrapper -sys32_get_kernel_syms_wrapper: - llgtr %r2,%r2 # struct kernel_sym_emu31 * - jg sys32_get_kernel_syms # branch to system call - .globl sys32_quotactl_wrapper sys32_quotactl_wrapper: lgfr %r2,%r2 # int @@ -786,15 +775,6 @@ sys32_getresuid16_wrapper: llgtr %r4,%r4 # __kernel_old_uid_emu31_t * jg sys32_getresuid16 # branch to system call - .globl sys32_query_module_wrapper -sys32_query_module_wrapper: - llgtr %r2,%r2 # const char * - lgfr %r3,%r3 # int - llgtr %r4,%r4 # char * - llgfr %r5,%r5 # size_t - llgtr %r6,%r6 # size_t * - jg sys32_query_module # branch to system call - .globl sys32_poll_wrapper sys32_poll_wrapper: llgtr %r2,%r2 # struct pollfd * diff --git a/arch/s390x/mm/extable.c b/arch/s390x/mm/extable.c index bd04ffbc28cc..be712ee65c24 100644 --- a/arch/s390x/mm/extable.c +++ b/arch/s390x/mm/extable.c @@ -2,10 +2,8 @@ * arch/s390/mm/extable.c * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Hartmut Penner (hp@de.ibm.com) * - * Derived from "arch/i386/mm/extable.c" + * identical to arch/i386/mm/extable.c */ #include @@ -42,24 +40,27 @@ extern spinlock_t modlist_lock; unsigned long search_exception_table(unsigned long addr) { + struct list_head *i; unsigned long ret = 0; - + #ifndef CONFIG_MODULES /* There is only the kernel to search. */ ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); return ret; #else unsigned long flags; - /* The kernel is the last "module" -- no need to treat it special. */ - struct module *mp; + struct list_head *i; + /* The kernel is the last "module" -- no need to treat it special. */ spin_lock_irqsave(&modlist_lock, flags); - for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING))) + list_for_each(i, &extables) { + struct exception_table *ex + = list_entry(i, struct exception_table, list); + if (ex->num_entries == 0) continue; - ret = search_one_table(mp->ex_table_start, - mp->ex_table_end - 1, addr); - if (ret) + ret = search_one_table(ex->entry, + ex->entry + ex->num_entries - 1, addr); + if (ret) break; } spin_unlock_irqrestore(&modlist_lock, flags); diff --git a/include/asm-s390/module.h b/include/asm-s390/module.h index 4f0759c1c92b..5453c0e73c27 100644 --- a/include/asm-s390/module.h +++ b/include/asm-s390/module.h @@ -4,9 +4,22 @@ * This file contains the s390 architecture specific module code. */ -#define module_map(x) vmalloc(x) -#define module_unmap(x) vfree(x) -#define module_arch_init(x) (0) -#define arch_init_modules(x) do { } while (0) +struct mod_arch_specific +{ + void *module_got, *module_plt; + unsigned long got_size, plt_size; +}; +#ifdef CONFIG_ARCH_S390X +#define ElfW(x) Elf64_ ## x +#define ELFW(x) ELF64_ ## x +#else +#define ElfW(x) Elf32_ ## x +#define ELFW(x) ELF32_ ## x +#endif + +#define Elf_Shdr ElfW(Shdr) +#define Elf_Sym ElfW(Sym) +#define Elf_Ehdr ElfW(Ehdr) +#define ELF_R_TYPE ELFW(R_TYPE) #endif /* _ASM_S390_MODULE_H */ diff --git a/include/asm-s390x/module.h b/include/asm-s390x/module.h index 4f0759c1c92b..d767a8db77c9 100644 --- a/include/asm-s390x/module.h +++ b/include/asm-s390x/module.h @@ -4,9 +4,22 @@ * This file contains the s390 architecture specific module code. */ -#define module_map(x) vmalloc(x) -#define module_unmap(x) vfree(x) -#define module_arch_init(x) (0) -#define arch_init_modules(x) do { } while (0) +struct mod_arch_specific +{ + void *module_got, *module_plt; + unsigned long got_size, plt_size; +}; +#ifdef CONFIG_ARCH_S390X +#define ElfW(x) Elf64_ ## x +#define ELFW(x) ELF64_ ## x +#else +#define ElfW(x) Elf32_ ## x +#define ELFW(x) ELF64_ ## x +#endif + +#define Elf_Shdr ElfW(Shdr) +#define Elf_Sym ElfW(Sym) +#define Elf_Ehdr ElfW(Ehdr) +#define ELF_R_TYPE ELFW(R_TYPE) #endif /* _ASM_S390_MODULE_H */ diff --git a/include/linux/elf.h b/include/linux/elf.h index 0fc8629ac459..c5863b39543c 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -413,6 +413,37 @@ typedef struct { /* Keep this the last entry. */ #define R_PPC_NUM 37 +/* s390 relocations defined by the ABIs */ +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +/* Keep this the last entry. */ +#define R_390_NUM 27 + /* Legal values for e_flags field of Elf64_Ehdr. */ #define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */ -- cgit v1.2.3 From 0466165bd11613f393f413da4301b7a8fb6b8611 Mon Sep 17 00:00:00 2001 From: James Morris Date: Sun, 17 Nov 2002 23:09:05 -0800 Subject: [CRYPTO]: Fix non-modular build. --- include/linux/crypto.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 35e3a20a2f5a..b3448683d02d 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -199,12 +199,7 @@ static inline const char *crypto_tfm_alg_name(struct crypto_tfm *tfm) static inline const char *crypto_tfm_alg_modname(struct crypto_tfm *tfm) { - struct crypto_alg *alg = tfm->__crt_alg; - - if (alg->cra_module) - return alg->cra_module->name; - else - return NULL; + return module_name(tfm->__crt_alg->cra_module); } static inline u32 crypto_tfm_alg_type(struct crypto_tfm *tfm) -- cgit v1.2.3 From 463191c2eb8f74de895dc36b34f33b394a1f8407 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 17 Nov 2002 23:40:25 -0800 Subject: [IPSEC]: Make xfrm_user key manager return proper errors. --- include/linux/netlink.h | 4 ++-- net/ipv4/xfrm_user.c | 6 ++---- net/netlink/af_netlink.c | 16 ++++++++++++---- 3 files changed, 16 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 6a0e792fb795..69346c394563 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -110,8 +110,8 @@ extern int init_netlink(void); extern struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len)); extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); -extern void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, - __u32 group, int allocation); +extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, + __u32 group, int allocation); extern void netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code); extern int netlink_register_notifier(struct notifier_block *nb); extern int netlink_unregister_notifier(struct notifier_block *nb); diff --git a/net/ipv4/xfrm_user.c b/net/ipv4/xfrm_user.c index e63611bfbf1d..fca48d755633 100644 --- a/net/ipv4/xfrm_user.c +++ b/net/ipv4/xfrm_user.c @@ -910,9 +910,8 @@ static int xfrm_send_notify(struct xfrm_state *x, int hard) BUG(); NETLINK_CB(skb).dst_groups = XFRMGRP_EXPIRE; - netlink_broadcast(xfrm_nl, skb, 0, XFRMGRP_EXPIRE, GFP_ATOMIC); - return 0; + return netlink_broadcast(xfrm_nl, skb, 0, XFRMGRP_EXPIRE, GFP_ATOMIC); } /* XXX Make this xfrm_state.c:xfrm_get_acqseq() */ @@ -971,9 +970,8 @@ static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, BUG(); NETLINK_CB(skb).dst_groups = XFRMGRP_ACQUIRE; - netlink_broadcast(xfrm_nl, skb, 0, XFRMGRP_ACQUIRE, GFP_ATOMIC); - return 0; + return netlink_broadcast(xfrm_nl, skb, 0, XFRMGRP_ACQUIRE, GFP_ATOMIC); } /* User gives us xfrm_user_policy_info followed by an array of 0 diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 73f6ccffe848..a7c5a350e6c4 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -503,13 +503,13 @@ static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff return -1; } -void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, - u32 group, int allocation) +int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, + u32 group, int allocation) { struct sock *sk; struct sk_buff *skb2 = NULL; int protocol = ssk->protocol; - int failure = 0; + int failure = 0, delivered = 0; /* While we sleep in clone, do not allow to change socket list */ @@ -544,8 +544,10 @@ void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, failure = 1; } else if (netlink_broadcast_deliver(sk, skb2)) { netlink_overrun(sk); - } else + } else { + delivered = 1; skb2 = NULL; + } sock_put(sk); } @@ -554,6 +556,12 @@ void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, if (skb2) kfree_skb(skb2); kfree_skb(skb); + + if (delivered) + return 0; + if (failure) + return -ENOBUFS; + return -ESRCH; } void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) -- cgit v1.2.3 From a5508ddcd000a435baa37f61ef1bdfb490fcf3c0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Nov 2002 02:48:17 -0800 Subject: [PATCH] kallsyms for new modules Since I believe kallsyms is important, this reimplements it sanely, using the current module infrastructure, and not using an external kallsyms script. FYI, the previous interface was: int kallsyms_symbol_to_address( const char *name, /* Name to lookup */ unsigned long *token, /* Which module to start with */ const char **mod_name, /* Set to module name or "kernel" */ unsigned long *mod_start, /* Set to start address of module */ unsigned long *mod_end, /* Set to end address of module */ const char **sec_name, /* Set to section name */ unsigned long *sec_start, /* Set to start address of section */ unsigned long *sec_end, /* Set to end address of section */ const char **sym_name, /* Set to full symbol name */ unsigned long *sym_start, /* Set to start address of symbol */ unsigned long *sym_end /* Set to end address of symbol */ ); The new one is: /* Lookup an address. modname is set to NULL if it's in the kernel. */ const char *kallsyms_lookup(unsigned long addr, unsigned long *symbolsize, unsigned long *offset, char **modname); --- Makefile | 4 +- arch/i386/Kconfig | 15 ++- arch/i386/kernel/process.c | 1 + arch/i386/kernel/traps.c | 1 + arch/ia64/Kconfig | 14 +-- arch/ppc/Kconfig | 6 +- arch/ppc/kernel/process.c | 1 + arch/x86_64/Kconfig | 14 +-- fs/proc/base.c | 18 ++- include/linux/kallsyms.h | 184 ++++++---------------------- include/linux/module.h | 25 +++- kernel/kallsyms.c | 291 +++++++++++++-------------------------------- kernel/module.c | 89 +++++++++++++- net/core/dev.c | 1 + scripts/kallsyms | 40 +++++++ 15 files changed, 302 insertions(+), 402 deletions(-) create mode 100644 scripts/kallsyms (limited to 'include/linux') diff --git a/Makefile b/Makefile index 9c0d573f94b6..2d97192ece66 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,7 @@ OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump AWK = awk GENKSYMS = /sbin/genksyms -KALLSYMS = /sbin/kallsyms +KALLSYMS = scripts/kallsyms PERL = perl MODFLAGS = -DMODULE CFLAGS_MODULE = $(MODFLAGS) @@ -335,7 +335,7 @@ ifdef CONFIG_KALLSYMS kallsyms.o := .tmp_kallsyms2.o quiet_cmd_kallsyms = KSYM $@ -cmd_kallsyms = $(KALLSYMS) $< > $@ +cmd_kallsyms = sh $(KALLSYMS) $< $@ .tmp_kallsyms1.o: .tmp_vmlinux1 $(call cmd,kallsyms) diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 0a5b43bf09ac..68b6c67d7044 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -1604,14 +1604,13 @@ config DEBUG_HIGHMEM This options enables addition error checking for high memory systems. Disable for production systems. -# Reimplemented RSN. -#config KALLSYMS -# bool "Load all symbols for debugging/kksymoops" -# depends on DEBUG_KERNEL -# help -# Say Y here to let the kernel print out symbolic crash information and -# symbolic stack backtraces. This increases the size of the kernel -# somewhat, as all symbols have to be loaded into the kernel image. +config KALLSYMS + bool "Load all symbols for debugging/kksymoops" + depends on DEBUG_KERNEL + help + Say Y here to let the kernel print out symbolic crash information and + symbolic stack backtraces. This increases the size of the kernel + somewhat, as all symbols have to be loaded into the kernel image. config X86_EXTRA_IRQS bool diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index f506696c73a0..d6640642ae3d 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index c462d415ae3f..7db903f53ed2 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef CONFIG_EISA #include diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index 37e458e99036..7708b29a0f90 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -813,13 +813,13 @@ config DEBUG_KERNEL Say Y here if you are developing drivers or trying to debug and identify kernel problems. -# config KALLSYMS -# bool "Load all symbols for debugging/kksymoops" -# depends on DEBUG_KERNEL -# help -# Say Y here to let the kernel print out symbolic crash information and -# symbolic stack backtraces. This increases the size of the kernel -# somewhat, as all symbols have to be loaded into the kernel image. +config KALLSYMS + bool "Load all symbols for debugging/kksymoops" + depends on DEBUG_KERNEL + help + Say Y here to let the kernel print out symbolic crash information and + symbolic stack backtraces. This increases the size of the kernel + somewhat, as all symbols have to be loaded into the kernel image. config IA64_PRINT_HAZARDS bool "Print possible IA-64 dependency violations to console" diff --git a/arch/ppc/Kconfig b/arch/ppc/Kconfig index 8dade2dcb054..793e02ff040a 100644 --- a/arch/ppc/Kconfig +++ b/arch/ppc/Kconfig @@ -1807,9 +1807,9 @@ config DEBUG_HIGHMEM bool "Highmem debugging" depends on DEBUG_KERNEL && HIGHMEM -# config KALLSYMS -# bool "Load all symbols for debugging/kksymoops" -# depends on DEBUG_KERNEL +config KALLSYMS + bool "Load all symbols for debugging/kksymoops" + depends on DEBUG_KERNEL config KGDB bool "Include kgdb kernel debugger" diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index d916a6623c44..c2aaff96a18a 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index e3dded7a439b..867d54bea665 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -739,13 +739,13 @@ config INIT_DEBUG help Fill __init and __initdata at the end of boot. This is only for debugging. -# config KALLSYMS -# bool "Load all symbols for debugging/kksymoops" -# depends on DEBUG_KERNEL -# help -# Say Y here to let the kernel print out symbolic crash information and -# symbolic stack backtraces. This increases the size of the kernel -# somewhat, as all symbols have to be loaded into the kernel image. +config KALLSYMS + bool "Load all symbols for debugging/kksymoops" + depends on DEBUG_KERNEL + help + Say Y here to let the kernel print out symbolic crash information and + symbolic stack backtraces. This increases the size of the kernel + somewhat, as all symbols have to be loaded into the kernel image. endmenu diff --git a/fs/proc/base.c b/fs/proc/base.c index 5fe9cada4a60..71d9984ad1dc 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -258,20 +258,18 @@ out: */ static int proc_pid_wchan(struct task_struct *task, char *buffer) { - const char *sym_name, *ignore; - unsigned long wchan, dummy; + char *modname; + const char *sym_name; + unsigned long wchan, size, offset; wchan = get_wchan(task); - if (!kallsyms_address_to_symbol(wchan, &ignore, &dummy, &dummy, - &ignore, &dummy, &dummy, &sym_name, - &dummy, &dummy)) { - return sprintf(buffer, "%lu", wchan); - } - - return sprintf(buffer, "%s", sym_name); + sym_name = kallsyms_lookup(wchan, &size, &offset, &modname); + if (sym_name) + return sprintf(buffer, "%s", sym_name); + return sprintf(buffer, "%lu", wchan); } -#endif +#endif /* CONFIG_KALLSYMS */ /************************************************************************/ /* Here the fs part begins */ diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h index 52226e6e158a..dcba85045308 100644 --- a/include/linux/kallsyms.h +++ b/include/linux/kallsyms.h @@ -1,163 +1,47 @@ -/* kallsyms headers - Copyright 2000 Keith Owens - - This file is part of the Linux modutils. It is exported to kernel - space so debuggers can access the kallsyms data. - - The kallsyms data contains all the non-stack symbols from a kernel - or a module. The kernel symbols are held between __start___kallsyms - and __stop___kallsyms. The symbols for a module are accessed via - the struct module chain which is based at module_list. - - 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. +/* Rewritten and vastly simplified by Rusty Russell for in-kernel + * module loader: + * Copyright 2002 Rusty Russell IBM Corporation */ +#ifndef _LINUX_KALLSYMS_H +#define _LINUX_KALLSYMS_H -#ident "$Id: linux-2.4.9-kallsyms.patch,v 1.8 2002/02/11 18:34:53 arjanv Exp $" - -#ifndef MODUTILS_KALLSYMS_H -#define MODUTILS_KALLSYMS_H 1 - -/* Have to (re)define these ElfW entries here because external kallsyms - * code does not have access to modutils/include/obj.h. This code is - * included from user spaces tools (modutils) and kernel, they need - * different includes. - */ - -#ifndef ELFCLASS32 -#ifdef __KERNEL__ -#include -#else /* __KERNEL__ */ -#include -#endif /* __KERNEL__ */ -#endif /* ELFCLASS32 */ - -#ifndef ELFCLASSM -#define ELFCLASSM ELF_CLASS -#endif - -#ifndef ElfW -# if ELFCLASSM == ELFCLASS32 -# define ElfW(x) Elf32_ ## x -# define ELFW(x) ELF32_ ## x -# else -# define ElfW(x) Elf64_ ## x -# define ELFW(x) ELF64_ ## x -# endif -#endif - -/* Format of data in the kallsyms section. - * Most of the fields are small numbers but the total size and all - * offsets can be large so use the 32/64 bit types for these fields. - * - * Do not use sizeof() on these structures, modutils may be using extra - * fields. Instead use the size fields in the header to access the - * other bits of data. - */ - -struct kallsyms_header { - int size; /* Size of this header */ - ElfW(Word) total_size; /* Total size of kallsyms data */ - int sections; /* Number of section entries */ - ElfW(Off) section_off; /* Offset to first section entry */ - int section_size; /* Size of one section entry */ - int symbols; /* Number of symbol entries */ - ElfW(Off) symbol_off; /* Offset to first symbol entry */ - int symbol_size; /* Size of one symbol entry */ - ElfW(Off) string_off; /* Offset to first string */ - ElfW(Addr) start; /* Start address of first section */ - ElfW(Addr) end; /* End address of last section */ -}; - -struct kallsyms_section { - ElfW(Addr) start; /* Start address of section */ - ElfW(Word) size; /* Size of this section */ - ElfW(Off) name_off; /* Offset to section name */ - ElfW(Word) flags; /* Flags from section */ -}; - -struct kallsyms_symbol { - ElfW(Off) section_off; /* Offset to section that owns this symbol */ - ElfW(Addr) symbol_addr; /* Address of symbol */ - ElfW(Off) name_off; /* Offset to symbol name */ -}; - -#define KALLSYMS_SEC_NAME "__kallsyms" -#define KALLSYMS_IDX 2 /* obj_kallsyms creates kallsyms as section 2 */ - -#define kallsyms_next_sec(h,s) \ - ((s) = (struct kallsyms_section *)((char *)(s) + (h)->section_size)) -#define kallsyms_next_sym(h,s) \ - ((s) = (struct kallsyms_symbol *)((char *)(s) + (h)->symbol_size)) +#include #ifdef CONFIG_KALLSYMS +/* Lookup an address. modname is set to NULL if it's in the kernel. */ +const char *kallsyms_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname); -int kallsyms_symbol_to_address( - const char *name, /* Name to lookup */ - unsigned long *token, /* Which module to start with */ - const char **mod_name, /* Set to module name or "kernel" */ - unsigned long *mod_start, /* Set to start address of module */ - unsigned long *mod_end, /* Set to end address of module */ - const char **sec_name, /* Set to section name */ - unsigned long *sec_start, /* Set to start address of section */ - unsigned long *sec_end, /* Set to end address of section */ - const char **sym_name, /* Set to full symbol name */ - unsigned long *sym_start, /* Set to start address of symbol */ - unsigned long *sym_end /* Set to end address of symbol */ - ); +/* Replace "%s" in format with address, if found */ +extern void __print_symbol(const char *fmt, unsigned long address); -int kallsyms_address_to_symbol( - unsigned long address, /* Address to lookup */ - const char **mod_name, /* Set to module name */ - unsigned long *mod_start, /* Set to start address of module */ - unsigned long *mod_end, /* Set to end address of module */ - const char **sec_name, /* Set to section name */ - unsigned long *sec_start, /* Set to start address of section */ - unsigned long *sec_end, /* Set to end address of section */ - const char **sym_name, /* Set to full symbol name */ - unsigned long *sym_start, /* Set to start address of symbol */ - unsigned long *sym_end /* Set to end address of symbol */ - ); +#else /* !CONFIG_KALLSYMS */ -int kallsyms_sections(void *token, - int (*callback)(void *, /* token */ - const char *, /* module name */ - const char *, /* section name */ - ElfW(Addr), /* Section start */ - ElfW(Addr), /* Section end */ - ElfW(Word) /* Section flags */ - ) - ); +static inline const char *kallsyms_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname) +{ + return NULL; +} -#else +/* Stupid that this does nothing, but I didn't create this mess. */ +#define __print_symbol(fmt, addr) +#endif /*CONFIG_KALLSYMS*/ -static inline int kallsyms_address_to_symbol( - unsigned long address, /* Address to lookup */ - const char **mod_name, /* Set to module name */ - unsigned long *mod_start, /* Set to start address of module */ - unsigned long *mod_end, /* Set to end address of module */ - const char **sec_name, /* Set to section name */ - unsigned long *sec_start, /* Set to start address of section */ - unsigned long *sec_end, /* Set to end address of section */ - const char **sym_name, /* Set to full symbol name */ - unsigned long *sym_start, /* Set to start address of symbol */ - unsigned long *sym_end /* Set to end address of symbol */ - ) +/* This macro allows us to keep printk typechecking */ +static void __check_printsym_format(const char *fmt, ...) +__attribute__((format(printf,1,2))); +static inline void __check_printsym_format(const char *fmt, ...) { - return -ESRCH; } -#endif +#define print_symbol(fmt, addr) \ +do { \ + __check_printsym_format(fmt, ""); \ + __print_symbol(fmt, addr); \ +} while(0) -#endif /* kallsyms.h */ +#endif /*_LINUX_KALLSYMS_H*/ diff --git a/include/linux/module.h b/include/linux/module.h index ff026ad568d1..4f440aa11f21 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -14,6 +14,8 @@ #include #include #include +#include + #include #include /* For struct exception_table_entry */ @@ -29,7 +31,6 @@ #define MODULE_GENERIC_TABLE(gtype,name) #define MODULE_DEVICE_TABLE(type,name) #define MODULE_PARM_DESC(var,desc) -#define print_symbol(format, addr) #define print_modules() #define MODULE_NAME_LEN (64 - sizeof(unsigned long)) @@ -137,6 +138,13 @@ struct module void (*exit)(void); #endif +#ifdef CONFIG_KALLSYMS + /* We keep the symbol and string tables for kallsyms. */ + Elf_Sym *symtab; + unsigned long num_syms; + char *strtab; +#endif + /* The command line arguments (may be mangled). People like keeping pointers to this stuff */ char args[0]; @@ -211,6 +219,12 @@ do { \ } \ } while(0) +/* For kallsyms to ask for address resolution. NULL means not found. */ +const char *module_address_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname); + #else /* !CONFIG_MODULES... */ #define EXPORT_SYMBOL(sym) #define EXPORT_SYMBOL_GPL(sym) @@ -227,6 +241,15 @@ do { \ #define module_name(mod) "kernel" #define __unsafe(mod) + +/* For kallsyms to ask for address resolution. NULL means not found. */ +static inline const char *module_address_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname) +{ + return NULL; +} #endif /* CONFIG_MODULES */ /* For archs to search exception tables */ diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 36d2da107b75..0335cc7fe4a1 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -1,233 +1,102 @@ /* - * kksymoops.c: in-kernel printing of symbolic oopses and stack traces. + * kallsyms.c: in-kernel printing of symbolic oopses and stack traces. * - * Copyright 2000 Keith Owens April 2000 - * Copyright 2002 Arjan van de Ven - * - This code uses the list of all kernel and module symbols to :- + * Rewritten and vastly simplified by Rusty Russell for in-kernel + * module loader: + * Copyright 2002 Rusty Russell IBM Corporation + */ +#include +#include - * Find any non-stack symbol in a kernel or module. Symbols do - not have to be exported for debugging. +static char kallsyms_dummy; - * Convert an address to the module (or kernel) that owns it, the - section it is in and the nearest symbol. This finds all non-stack - symbols, not just exported ones. +/* These will be re-linked against their real values during the second link stage */ +extern unsigned long kallsyms_addresses[1] __attribute__((weak, alias("kallsyms_dummy"))); +extern unsigned long kallsyms_num_syms __attribute__((weak, alias("kallsyms_dummy"))); +extern char kallsyms_names[1] __attribute__((weak, alias("kallsyms_dummy"))); - */ +/* Defined by the linker script. */ +extern char _stext[], _etext[]; -#include -#include -#include +/* Lookup an address. modname is set to NULL if it's in the kernel. */ +const char *kallsyms_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname) +{ + unsigned long i, best = 0; -/* A symbol can appear in more than one module. A token is used to - * restart the scan at the next module, set the token to 0 for the - * first scan of each symbol. - */ + /* This kernel should never had been booted. */ + if ((void *)kallsyms_addresses == &kallsyms_dummy) + BUG(); -int kallsyms_symbol_to_address( - const char *name, /* Name to lookup */ - unsigned long *token, /* Which module to start at */ - const char **mod_name, /* Set to module name */ - unsigned long *mod_start, /* Set to start address of module */ - unsigned long *mod_end, /* Set to end address of module */ - const char **sec_name, /* Set to section name */ - unsigned long *sec_start, /* Set to start address of section */ - unsigned long *sec_end, /* Set to end address of section */ - const char **sym_name, /* Set to full symbol name */ - unsigned long *sym_start, /* Set to start address of symbol */ - unsigned long *sym_end /* Set to end address of symbol */ - ) -{ - const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ - const struct kallsyms_section *ka_sec; - const struct kallsyms_symbol *ka_sym = NULL; - const char *ka_str = NULL; - const struct module *m; - int i = 0, l; - const char *p, *pt_R; - char *p2; - - /* Restart? */ - m = module_list; - if (token && *token) { - for (; m; m = m->next) - if ((unsigned long)m == *token) - break; - if (m) - m = m->next; - } + if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) { + unsigned long symbol_end; + char *name = kallsyms_names; - for (; m; m = m->next) { - if (!mod_member_present(m, kallsyms_start) || - !mod_member_present(m, kallsyms_end) || - m->kallsyms_start >= m->kallsyms_end) - continue; - ka_hdr = (struct kallsyms_header *)m->kallsyms_start; - ka_sym = (struct kallsyms_symbol *) - ((char *)(ka_hdr) + ka_hdr->symbol_off); - ka_str = - ((char *)(ka_hdr) + ka_hdr->string_off); - for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) { - p = ka_str + ka_sym->name_off; - if (strcmp(p, name) == 0) - break; - /* Unversioned requests match versioned names */ - if (!(pt_R = strstr(p, "_R"))) - continue; - l = strlen(pt_R); - if (l < 10) - continue; /* Not _R.*xxxxxxxx */ - (void)simple_strtoul(pt_R+l-8, &p2, 16); - if (*p2) - continue; /* Not _R.*xxxxxxxx */ - if (strncmp(p, name, pt_R-p) == 0) - break; /* Match with version */ + /* They're sorted, we could be clever here, but who cares? */ + for (i = 0; i < kallsyms_num_syms; i++) { + if (kallsyms_addresses[i] > kallsyms_addresses[best] && + kallsyms_addresses[i] <= addr) + best = i; } - if (i < ka_hdr->symbols) - break; - } - if (token) - *token = (unsigned long)m; - if (!m) - return(0); /* not found */ - - ka_sec = (const struct kallsyms_section *) - ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off); - *mod_name = m->name; - *mod_start = ka_hdr->start; - *mod_end = ka_hdr->end; - *sec_name = ka_sec->name_off + ka_str; - *sec_start = ka_sec->start; - *sec_end = ka_sec->start + ka_sec->size; - *sym_name = ka_sym->name_off + ka_str; - *sym_start = ka_sym->symbol_addr; - if (i < ka_hdr->symbols-1) { - const struct kallsyms_symbol *ka_symn = ka_sym; - kallsyms_next_sym(ka_hdr, ka_symn); - *sym_end = ka_symn->symbol_addr; - } - else - *sym_end = *sec_end; - return(1); -} + /* Grab name */ + for (i = 0; i < best; i++) + name += strlen(name)+1; -int kallsyms_address_to_symbol( - unsigned long address, /* Address to lookup */ - const char **mod_name, /* Set to module name */ - unsigned long *mod_start, /* Set to start address of module */ - unsigned long *mod_end, /* Set to end address of module */ - const char **sec_name, /* Set to section name */ - unsigned long *sec_start, /* Set to start address of section */ - unsigned long *sec_end, /* Set to end address of section */ - const char **sym_name, /* Set to full symbol name */ - unsigned long *sym_start, /* Set to start address of symbol */ - unsigned long *sym_end /* Set to end address of symbol */ - ) -{ - const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ - const struct kallsyms_section *ka_sec = NULL; - const struct kallsyms_symbol *ka_sym; - const char *ka_str; - const struct module *m; - int i; - unsigned long end; - - for (m = module_list; m; m = m->next) { - - if (!mod_member_present(m, kallsyms_start) || - !mod_member_present(m, kallsyms_end) || - m->kallsyms_start >= m->kallsyms_end) - continue; - ka_hdr = (struct kallsyms_header *)m->kallsyms_start; - ka_sec = (const struct kallsyms_section *) - ((char *)ka_hdr + ka_hdr->section_off); - /* Is the address in any section in this module? */ - for (i = 0; i < ka_hdr->sections; ++i, kallsyms_next_sec(ka_hdr, ka_sec)) { - if (ka_sec->start <= address && - (ka_sec->start + ka_sec->size) > address) + /* Base symbol size on next symbol, but beware aliases. */ + symbol_end = (unsigned long)_etext; + for (i = best+1; i < kallsyms_num_syms; i++) { + if (kallsyms_addresses[i] != kallsyms_addresses[best]){ + symbol_end = kallsyms_addresses[i]; break; + } } - if (i < ka_hdr->sections) - break; /* Found a matching section */ - } - if (!m) - return(0); /* not found */ - - ka_sym = (struct kallsyms_symbol *) - ((char *)(ka_hdr) + ka_hdr->symbol_off); - ka_str = - ((char *)(ka_hdr) + ka_hdr->string_off); - *mod_name = m->name; - *mod_start = ka_hdr->start; - *mod_end = ka_hdr->end; - *sec_name = ka_sec->name_off + ka_str; - *sec_start = ka_sec->start; - *sec_end = ka_sec->start + ka_sec->size; - *sym_name = *sec_name; /* In case we find no matching symbol */ - *sym_start = *sec_start; - *sym_end = *sec_end; - - for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) { - if (ka_sym->symbol_addr > address) - continue; - if (i < ka_hdr->symbols-1) { - const struct kallsyms_symbol *ka_symn = ka_sym; - kallsyms_next_sym(ka_hdr, ka_symn); - end = ka_symn->symbol_addr; - } - else - end = *sec_end; - if (end <= address) - continue; - if ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off - != (char *)ka_sec) - continue; /* wrong section */ - *sym_name = ka_str + ka_sym->name_off; - *sym_start = ka_sym->symbol_addr; - *sym_end = end; - break; + *symbolsize = symbol_end - kallsyms_addresses[best]; + *modname = NULL; + *offset = addr - kallsyms_addresses[best]; + return name; } - return(1); + + return module_address_lookup(addr, symbolsize, offset, modname); } -/* List all sections in all modules. The callback routine is invoked with - * token, module name, section name, section start, section end, section flags. - */ -int kallsyms_sections(void *token, - int (*callback)(void *, const char *, const char *, ElfW(Addr), ElfW(Addr), ElfW(Word))) +/* Replace "%s" in format with address, or returns -errno. */ +void __print_symbol(const char *fmt, unsigned long address) { - const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ - const struct kallsyms_section *ka_sec = NULL; - const char *ka_str; - const struct module *m; - int i; - - for (m = module_list; m; m = m->next) { - if (!mod_member_present(m, kallsyms_start) || - !mod_member_present(m, kallsyms_end) || - m->kallsyms_start >= m->kallsyms_end) - continue; - ka_hdr = (struct kallsyms_header *)m->kallsyms_start; - ka_sec = (const struct kallsyms_section *) ((char *)ka_hdr + ka_hdr->section_off); - ka_str = ((char *)(ka_hdr) + ka_hdr->string_off); - for (i = 0; i < ka_hdr->sections; ++i, kallsyms_next_sec(ka_hdr, ka_sec)) { - if (callback( - token, - *(m->name) ? m->name : "kernel", - ka_sec->name_off + ka_str, - ka_sec->start, - ka_sec->start + ka_sec->size, - ka_sec->flags)) - return(0); - } + char *modname; + const char *name; + unsigned long offset, size; + + name = kallsyms_lookup(address, &size, &offset, &modname); + + if (!name) { + char addrstr[sizeof("0x%lx") + (BITS_PER_LONG*3/10)]; + + sprintf(addrstr, "0x%lx", address); + printk(fmt, addrstr); + return; } - return(1); -} -/* Allocate the __kallsyms section, so it's already present in - * the temporary vmlinux that kallsyms is run on, so the first - * run will pick up the section info already. */ + if (modname) { + /* This is pretty small. */ + char buffer[sizeof("%s+%#lx/%#lx [%s]") + + strlen(name) + 2*(BITS_PER_LONG*3/10) + + strlen(modname)]; + + sprintf(buffer, "%s+%#lx/%#lx [%s]", + name, offset, size, modname); + printk(fmt, buffer); + } else { + char buffer[sizeof("%s+%#lx/%#lx") + + strlen(name) + 2*(BITS_PER_LONG*3/10)]; + + sprintf(buffer, "%s+%#lx/%#lx", name, offset, size); + printk(fmt, buffer); + } +} -__asm__(".section __kallsyms,\"a\"\n.previous"); +EXPORT_SYMBOL(kallsyms_lookup); +EXPORT_SYMBOL(__print_symbol); diff --git a/kernel/module.c b/kernel/module.c index 82aa7ebe6694..464c3d9320b4 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -898,6 +898,11 @@ static struct module *load_module(void *umod, DEBUGP("Exception table found in section %u\n", i); exindex = i; } +#ifdef CONFIG_KALLSYMS + /* symbol and string tables for decoding later. */ + if (sechdrs[i].sh_type == SHT_SYMTAB || i == hdr->e_shstrndx) + sechdrs[i].sh_flags |= SHF_ALLOC; +#endif #ifndef CONFIG_MODULE_UNLOAD /* Don't load .exit sections */ if (strstr(secstrings+sechdrs[i].sh_name, ".exit")) @@ -1026,6 +1031,11 @@ static struct module *load_module(void *umod, goto cleanup; } +#ifdef CONFIG_KALLSYMS + mod->symtab = (void *)sechdrs[symindex].sh_offset; + mod->num_syms = sechdrs[symindex].sh_size / sizeof(Elf_Sym); + mod->strtab = (void *)sechdrs[strindex].sh_offset; +#endif err = module_finalize(hdr, sechdrs, mod); if (err < 0) goto cleanup; @@ -1141,9 +1151,82 @@ sys_init_module(void *umod, return 0; } -/* Called by the /proc file system to return a current list of - modules. Al Viro came up with this interface as an "improvement". - God save us from any more such interface improvements. */ +#ifdef CONFIG_KALLSYMS +static inline int inside_init(struct module *mod, unsigned long addr) +{ + if (mod->module_init + && (unsigned long)mod->module_init <= addr + && (unsigned long)mod->module_init + mod->init_size > addr) + return 1; + return 0; +} + +static inline int inside_core(struct module *mod, unsigned long addr) +{ + if ((unsigned long)mod->module_core <= addr + && (unsigned long)mod->module_core + mod->core_size > addr) + return 1; + return 0; +} + +static const char *get_ksymbol(struct module *mod, + unsigned long addr, + unsigned long *size, + unsigned long *offset) +{ + unsigned int i, next = 0, best = 0; + + /* Scan for closest preceeding symbol, and next symbol. (ELF + starts real symbols at 1). */ + for (i = 1; i < mod->num_syms; i++) { + if (mod->symtab[i].st_shndx == SHN_UNDEF) + continue; + + if (mod->symtab[i].st_value <= addr + && mod->symtab[i].st_value > mod->symtab[best].st_value) + best = i; + if (mod->symtab[i].st_value > addr + && mod->symtab[i].st_value < mod->symtab[next].st_value) + next = i; + } + + if (!best) + return NULL; + + if (!next) { + /* Last symbol? It ends at the end of the module then. */ + if (inside_core(mod, addr)) + *size = mod->module_core+mod->core_size - (void*)addr; + else + *size = mod->module_init+mod->init_size - (void*)addr; + } else + *size = mod->symtab[next].st_value - addr; + + *offset = addr - mod->symtab[best].st_value; + return mod->strtab + mod->symtab[best].st_name; +} + +/* For kallsyms to ask for address resolution. NULL means not found. + We don't lock, as this is used for oops resolution and races are a + lesser concern. */ +const char *module_address_lookup(unsigned long addr, + unsigned long *size, + unsigned long *offset, + char **modname) +{ + struct module *mod; + + list_for_each_entry(mod, &modules, list) { + if (inside_core(mod, addr) || inside_init(mod, addr)) { + *modname = mod->name; + return get_ksymbol(mod, addr, size, offset); + } + } + return NULL; +} +#endif /* CONFIG_KALLSYMS */ + +/* Called by the /proc file system to return a list of modules. */ static void *m_start(struct seq_file *m, loff_t *pos) { struct list_head *i; diff --git a/net/core/dev.c b/net/core/dev.c index 4206f99b19b3..20b59c270471 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -105,6 +105,7 @@ #include #include #include +#include #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) #include /* Note : will define WIRELESS_EXT */ #include diff --git a/scripts/kallsyms b/scripts/kallsyms new file mode 100644 index 000000000000..db057d0974dc --- /dev/null +++ b/scripts/kallsyms @@ -0,0 +1,40 @@ +#! /bin/sh +# Written by Rusty Russell 2002. + +if [ $# -ne 2 ]; then + echo Usage: kallsyms vmlinux objfile >&2 + + echo Adds a .kallsyms section containing symbol info. + exit 1 +fi + +set -e + +# Clean up on exit. +trap "rm -f kallsyms.map kallsyms.c $2" 0 + +# Takes nm output from $1, produces a .c file on standard output. +encode_symbols() +{ + # First take addresses. + echo "unsigned long kallsyms_addresses[] = {" + sed 's/^[ ]*\([A-Fa-f0-9]*\).*/0x\1UL,/' < $1 + echo "};" + + # Now output size. + echo "unsigned long kallsyms_num_syms = `wc -l < $1`;" + + # Now output names. + echo "char kallsyms_names[] = "; + sed 's/^[ ]*[A-Fa-f0-9]*[ ]*.[ ]\(.*\)/"\1\\0"/' < $1 + echo ";" +} + +# FIXME: Use System.map as input, and regenerate each time in Makefile. +$NM $1 | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > kallsyms.map + +encode_symbols kallsyms.map > kallsyms.c +$CC $CFLAGS -c -o $2 kallsyms.c + +trap "rm -f kallsyms.map kallsyms.c" 0 +exit 0 -- cgit v1.2.3 From 0788fd347906928fcfef52fba2fbb188066daaa1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 18 Nov 2002 04:51:17 -0800 Subject: Parts of "module.c" was needed even when no module support was enabled, so split it up into "extable.c" --- include/linux/module.h | 39 +++++++++++++++++++++++--------------- kernel/Makefile | 2 +- kernel/extable.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ kernel/module.c | 30 +---------------------------- 4 files changed, 77 insertions(+), 45 deletions(-) create mode 100644 kernel/extable.c (limited to 'include/linux') diff --git a/include/linux/module.h b/include/linux/module.h index 4f440aa11f21..47ff2bc63e33 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -49,21 +49,6 @@ extern struct module __this_module; #define THIS_MODULE ((struct module *)0) #endif -#ifdef CONFIG_MODULES -/* Get/put a kernel symbol (calls must be symmetric) */ -void *__symbol_get(const char *symbol); -void *__symbol_get_gpl(const char *symbol); -#define symbol_get(x) ((typeof(&x))(__symbol_get(#x))) - -/* For every exported symbol, place a struct in the __ksymtab section */ -#define EXPORT_SYMBOL(sym) \ - const struct kernel_symbol __ksymtab_##sym \ - __attribute__((section("__ksymtab"))) \ - = { (unsigned long)&sym, #sym } - -#define EXPORT_SYMBOL_NOVERS(sym) EXPORT_SYMBOL(sym) -#define EXPORT_SYMBOL_GPL(sym) EXPORT_SYMBOL(sym) - struct kernel_symbol_group { /* Links us into the global symbol list */ @@ -84,6 +69,22 @@ struct exception_table const struct exception_table_entry *entry; }; + +#ifdef CONFIG_MODULES +/* Get/put a kernel symbol (calls must be symmetric) */ +void *__symbol_get(const char *symbol); +void *__symbol_get_gpl(const char *symbol); +#define symbol_get(x) ((typeof(&x))(__symbol_get(#x))) + +/* For every exported symbol, place a struct in the __ksymtab section */ +#define EXPORT_SYMBOL(sym) \ + const struct kernel_symbol __ksymtab_##sym \ + __attribute__((section("__ksymtab"))) \ + = { (unsigned long)&sym, #sym } + +#define EXPORT_SYMBOL_NOVERS(sym) EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL_GPL(sym) EXPORT_SYMBOL(sym) + struct module_ref { atomic_t count; @@ -302,6 +303,14 @@ extern int module_dummy_usage; #define cleanup_module(voidarg) __exitfn(void) #endif +/* + * The exception and symbol tables, and the lock + * to protect them. + */ +extern spinlock_t modlist_lock; +extern struct list_head extables; +extern struct list_head symbols; + /* Use symbol_get and symbol_put instead. You'll thank me. */ #define HAVE_INTER_MODULE extern void inter_module_register(const char *, struct module *, const void *); diff --git a/kernel/Makefile b/kernel/Makefile index bc0f6371f222..cddad55cac0d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -10,7 +10,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ exit.o itimer.o time.o softirq.o resource.o \ sysctl.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o workqueue.o futex.o platform.o pid.o \ - rcupdate.o intermodule.o + rcupdate.o intermodule.o extable.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o obj-$(CONFIG_SMP) += cpu.o diff --git a/kernel/extable.c b/kernel/extable.c new file mode 100644 index 000000000000..e26ef5349d23 --- /dev/null +++ b/kernel/extable.c @@ -0,0 +1,51 @@ +/* Rewritten by Rusty Russell, on the backs of many others... + Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM. + + 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 + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; +extern const struct kernel_symbol __start___ksymtab[]; +extern const struct kernel_symbol __stop___ksymtab[]; + +/* Protects extables and symbol tables */ +spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED; + +/* The exception and symbol tables: start with kernel only. */ +LIST_HEAD(extables); +LIST_HEAD(symbols); + +static struct exception_table kernel_extable; +static struct kernel_symbol_group kernel_symbols; + +void __init extable_init(void) +{ + /* Add kernel symbols to symbol table */ + kernel_symbols.num_syms = (__stop___ksymtab - __start___ksymtab); + kernel_symbols.syms = __start___ksymtab; + list_add(&kernel_symbols.list, &symbols); + + /* Add kernel exception table to exception tables */ + kernel_extable.num_entries = (__stop___ex_table -__start___ex_table); + kernel_extable.entry = __start___ex_table; + list_add(&kernel_extable.list, &extables); +} + + diff --git a/kernel/module.c b/kernel/module.c index 464c3d9320b4..1d8410b0569f 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -37,21 +37,6 @@ #define DEBUGP(fmt , a...) #endif -extern const struct exception_table_entry __start___ex_table[]; -extern const struct exception_table_entry __stop___ex_table[]; -extern const struct kernel_symbol __start___ksymtab[]; -extern const struct kernel_symbol __stop___ksymtab[]; - -/* Protects extables and symbol tables */ -spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED; - -/* The exception and symbol tables: start with kernel only. */ -LIST_HEAD(extables); -static LIST_HEAD(symbols); - -static struct exception_table kernel_extable; -static struct kernel_symbol_group kernel_symbols; - /* List of modules, protected by module_mutex */ static DECLARE_MUTEX(module_mutex); LIST_HEAD(modules); /* FIXME: Accessed w/o lock on oops by some archs */ @@ -1139,7 +1124,7 @@ sys_init_module(void *umod, /* Now it's a first class citizen! */ spin_lock_irq(&modlist_lock); - list_add(&mod->symbols.list, &kernel_symbols.list); + list_add_tail(&mod->symbols.list, &symbols); spin_unlock_irq(&modlist_lock); list_add(&mod->list, &modules); @@ -1271,19 +1256,6 @@ struct seq_operations modules_op = { .show = m_show }; -void __init extable_init(void) -{ - /* Add kernel symbols to symbol table */ - kernel_symbols.num_syms = (__stop___ksymtab - __start___ksymtab); - kernel_symbols.syms = __start___ksymtab; - list_add(&kernel_symbols.list, &symbols); - - /* Add kernel exception table to exception tables */ - kernel_extable.num_entries = (__stop___ex_table -__start___ex_table); - kernel_extable.entry = __start___ex_table; - list_add(&kernel_extable.list, &extables); -} - /* Obsolete lvalue for broken code which asks about usage */ int module_dummy_usage = 1; EXPORT_SYMBOL(module_dummy_usage); -- cgit v1.2.3 From 448cfb187d97284effb90217e004ef99ad8e8c38 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 18 Nov 2002 06:27:49 -0800 Subject: [PATCH] Support for micro/nanosecond [acm]time in the NFS client Given Andi Kleen's patch that introduces of VFS-level support for nanosecond time resolutions, we should finally be able to export the existing NFS client level support to userland. In order to do so, the following patch will convert all NFS private 'raw u64' values into the kernel-supported struct timespec directly in the xdr_encode/xdr_decode routines. It adds support for the nanosecond field in NFS 'setattr' calls, and in nfs_refresh_inode(). Finally, there are a few cleanups in the nfs_refresh_inode code that convert multiple use of the NFS_*(inode) macros into a single dereference of NFS_I(inode). --- fs/nfs/inode.c | 122 ++++++++++++++++++++++-------------------------- fs/nfs/nfs2xdr.c | 22 ++++++--- fs/nfs/nfs3xdr.c | 26 ++++------- fs/nfs/nfs4proc.c | 8 ++-- fs/nfs/nfs4xdr.c | 10 ++-- include/linux/nfs_fs.h | 18 +------ include/linux/nfs_xdr.h | 12 ++--- 7 files changed, 98 insertions(+), 120 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 8525ab547458..207401451f9f 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -656,9 +656,7 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) goto out_no_inode; if (inode->i_state & I_NEW) { - __u64 new_size, new_mtime; - loff_t new_isize; - time_t new_atime; + struct nfs_inode *nfsi = NFS_I(inode); /* We set i_ino for the few things that still rely on it, * such as stat(2) */ @@ -686,24 +684,17 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) else init_special_inode(inode, inode->i_mode, fattr->rdev); - new_mtime = fattr->mtime; - new_size = fattr->size; - new_isize = nfs_size_to_loff_t(fattr->size); - new_atime = nfs_time_to_secs(fattr->atime); - - NFS_READTIME(inode) = fattr->timestamp; - NFS_CACHE_CTIME(inode) = fattr->ctime; - inode->i_ctime.tv_sec = nfs_time_to_secs(fattr->ctime); - inode->i_ctime.tv_nsec = nfs_time_to_nsecs(fattr->ctime); - inode->i_atime.tv_sec = new_atime; - NFS_CACHE_MTIME(inode) = new_mtime; - inode->i_mtime.tv_sec = nfs_time_to_secs(new_mtime); - inode->i_mtime.tv_nsec = nfs_time_to_nsecs(new_mtime); - NFS_MTIME_UPDATE(inode) = fattr->timestamp; - NFS_CACHE_ISIZE(inode) = new_size; + nfsi->read_cache_jiffies = fattr->timestamp; + inode->i_atime = fattr->atime; + inode->i_mtime = fattr->mtime; + inode->i_ctime = fattr->ctime; + nfsi->read_cache_ctime = fattr->ctime; + nfsi->read_cache_mtime = fattr->mtime; + nfsi->cache_mtime_jiffies = fattr->timestamp; + nfsi->read_cache_isize = fattr->size; if (fattr->valid & NFS_ATTR_FATTR_V4) - NFS_CHANGE_ATTR(inode) = fattr->change_attr; - inode->i_size = new_isize; + nfsi->change_attr = fattr->change_attr; + inode->i_size = nfs_size_to_loff_t(fattr->size); inode->i_mode = fattr->mode; inode->i_nlink = fattr->nlink; inode->i_uid = fattr->uid; @@ -718,10 +709,10 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) inode->i_blocks = fattr->du.nfs2.blocks; inode->i_blksize = fattr->du.nfs2.blocksize; } - NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); - NFS_ATTRTIMEO_UPDATE(inode) = jiffies; - memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); - NFS_I(inode)->cache_access.cred = NULL; + nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); + nfsi->attrtimeo_timestamp = jiffies; + memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); + nfsi->cache_access.cred = NULL; unlock_new_inode(inode); } else @@ -787,9 +778,10 @@ printk("nfs_setattr: revalidate failed, error=%d\n", error); * now to avoid invalidating the page cache. */ if (!(fattr.valid & NFS_ATTR_WCC)) { - fattr.pre_size = NFS_CACHE_ISIZE(inode); - fattr.pre_mtime = NFS_CACHE_MTIME(inode); - fattr.pre_ctime = NFS_CACHE_CTIME(inode); + struct nfs_inode *nfsi = NFS_I(inode); + fattr.pre_size = nfsi->read_cache_isize; + fattr.pre_mtime = nfsi->read_cache_mtime; + fattr.pre_ctime = nfsi->read_cache_ctime; fattr.valid |= NFS_ATTR_WCC; } /* Force an attribute cache update */ @@ -962,14 +954,18 @@ out: static inline int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr) { - s64 cdif; + struct nfs_inode *nfsi = NFS_I(inode); + long cdif; - if (time_after(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) + if (time_after(jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo)) goto out_valid; - if ((cdif = (s64)fattr->ctime - (s64)NFS_CACHE_CTIME(inode)) > 0) + cdif = fattr->ctime.tv_sec - nfsi->read_cache_ctime.tv_sec; + if (cdif == 0) + cdif = fattr->ctime.tv_nsec - nfsi->read_cache_ctime.tv_nsec; + if (cdif > 0) goto out_valid; /* Ugh... */ - if (cdif == 0 && fattr->size > NFS_CACHE_ISIZE(inode)) + if (cdif == 0 && fattr->size > nfsi->read_cache_isize) goto out_valid; return -1; out_valid: @@ -991,19 +987,20 @@ int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr) int __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) { - __u64 new_size, new_mtime; + struct nfs_inode *nfsi = NFS_I(inode); + __u64 new_size; loff_t new_isize; - struct timespec new_atime; int invalid = 0; + int mtime_update = 0; dfprintk(VFS, "NFS: refresh_inode(%s/%ld ct=%d info=0x%x)\n", inode->i_sb->s_id, inode->i_ino, atomic_read(&inode->i_count), fattr->valid); - if (NFS_FILEID(inode) != fattr->fileid) { + if (nfsi->fileid != fattr->fileid) { printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n" "expected (%s/0x%Lx), got (%s/0x%Lx)\n", - inode->i_sb->s_id, (long long)NFS_FILEID(inode), + inode->i_sb->s_id, (long long)nfsi->fileid, inode->i_sb->s_id, (long long)fattr->fileid); goto out_err; } @@ -1017,12 +1014,9 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) goto out_changed; - new_mtime = fattr->mtime; new_size = fattr->size; new_isize = nfs_size_to_loff_t(fattr->size); - new_atime.tv_sec = nfs_time_to_secs(fattr->atime); - new_atime.tv_nsec = nfs_time_to_nsecs(fattr->atime); /* Avoid races */ if (nfs_fattr_obsolete(inode, fattr)) goto out_nochange; @@ -1030,13 +1024,13 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) /* * Update the read time so we don't revalidate too often. */ - NFS_READTIME(inode) = fattr->timestamp; + nfsi->read_cache_jiffies = fattr->timestamp; /* * Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache. * NOT inode->i_size!!! */ - if (NFS_CACHE_ISIZE(inode) != new_size) { + if (nfsi->read_cache_isize != new_size) { #ifdef NFS_DEBUG_VERBOSE printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); #endif @@ -1048,15 +1042,16 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) * can change this value in VFS without requiring a * cache revalidation. */ - if (NFS_CACHE_MTIME(inode) != new_mtime) { + if (!timespec_equal(&nfsi->read_cache_mtime, &fattr->mtime)) { #ifdef NFS_DEBUG_VERBOSE printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); #endif invalid = 1; + mtime_update = 1; } if ((fattr->valid & NFS_ATTR_FATTR_V4) - && NFS_CHANGE_ATTR(inode) != fattr->change_attr) { + && nfsi->change_attr != fattr->change_attr) { #ifdef NFS_DEBUG_VERBOSE printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); @@ -1070,12 +1065,12 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) * operation, so there's no need to invalidate the caches. */ if ((fattr->valid & NFS_ATTR_PRE_CHANGE) - && NFS_CHANGE_ATTR(inode) == fattr->pre_change_attr) { + && nfsi->change_attr == fattr->pre_change_attr) { invalid = 0; } else if ((fattr->valid & NFS_ATTR_WCC) - && NFS_CACHE_ISIZE(inode) == fattr->pre_size - && NFS_CACHE_MTIME(inode) == fattr->pre_mtime) { + && nfsi->read_cache_isize == fattr->pre_size + && timespec_equal(&nfsi->read_cache_mtime, &fattr->pre_mtime)) { invalid = 0; } @@ -1086,21 +1081,18 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) if (nfs_have_writebacks(inode) && new_isize < inode->i_size) new_isize = inode->i_size; - NFS_CACHE_CTIME(inode) = fattr->ctime; - inode->i_ctime.tv_sec = nfs_time_to_secs(fattr->ctime); - inode->i_ctime.tv_nsec = nfs_time_to_nsecs(fattr->ctime); - - inode->i_atime = new_atime; + nfsi->read_cache_ctime = fattr->ctime; + inode->i_ctime = fattr->ctime; + inode->i_atime = fattr->atime; - if (NFS_CACHE_MTIME(inode) != new_mtime) { + if (mtime_update) { if (invalid) - NFS_MTIME_UPDATE(inode) = fattr->timestamp; - NFS_CACHE_MTIME(inode) = new_mtime; - inode->i_mtime.tv_sec = nfs_time_to_secs(new_mtime); - inode->i_mtime.tv_nsec = nfs_time_to_nsecs(new_mtime); + nfsi->cache_mtime_jiffies = fattr->timestamp; + nfsi->read_cache_mtime = fattr->mtime; + inode->i_mtime = fattr->mtime; } - NFS_CACHE_ISIZE(inode) = new_size; + nfsi->read_cache_isize = new_size; inode->i_size = new_isize; if (inode->i_mode != fattr->mode || @@ -1114,7 +1106,7 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) } if (fattr->valid & NFS_ATTR_FATTR_V4) - NFS_CHANGE_ATTR(inode) = fattr->change_attr; + nfsi->change_attr = fattr->change_attr; inode->i_mode = fattr->mode; inode->i_nlink = fattr->nlink; @@ -1134,20 +1126,20 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) /* Update attrtimeo value */ if (invalid) { - NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); - NFS_ATTRTIMEO_UPDATE(inode) = jiffies; + nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); + nfsi->attrtimeo_timestamp = jiffies; invalidate_inode_pages(inode->i_mapping); memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); - } else if (time_after(jiffies, NFS_ATTRTIMEO_UPDATE(inode)+NFS_ATTRTIMEO(inode))) { - if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode)) - NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode); - NFS_ATTRTIMEO_UPDATE(inode) = jiffies; + } else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) { + if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) + nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); + nfsi->attrtimeo_timestamp = jiffies; } return 0; out_nochange: - if (!timespec_equal(&new_atime, &inode->i_atime)) - inode->i_atime = new_atime; + if (!timespec_equal(&fattr->atime, &inode->i_atime)) + inode->i_atime = fattr->atime; return 0; out_changed: /* diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 24af40d7e524..f2bc561dcfe8 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -86,10 +86,20 @@ xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle) } static inline u32* -xdr_decode_time(u32 *p, u64 *timep) +xdr_encode_time(u32 *p, struct timespec *timep) { - u64 tmp = (u64)ntohl(*p++) << 32; - *timep = tmp + (u64)ntohl(*p++); + *p++ = htonl(timep->tv_sec); + /* Convert nanoseconds into microseconds */ + *p++ = htonl(timep->tv_nsec / 1000); + return p; +} + +static inline u32* +xdr_decode_time(u32 *p, struct timespec *timep) +{ + timep->tv_sec = ntohl(*p++); + /* Convert microseconds into nanoseconds */ + timep->tv_nsec = ntohl(*p++) * 1000; return p; } @@ -131,16 +141,14 @@ xdr_encode_sattr(u32 *p, struct iattr *attr) SATTR(p, attr, ATTR_SIZE, ia_size); if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) { - *p++ = htonl(attr->ia_atime.tv_sec); - *p++ = 0; + p = xdr_encode_time(p, &attr->ia_atime); } else { *p++ = ~(u32) 0; *p++ = ~(u32) 0; } if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) { - *p++ = htonl(attr->ia_mtime.tv_sec); - *p++ = 0; + p = xdr_encode_time(p, &attr->ia_mtime); } else { *p++ = ~(u32) 0; *p++ = ~(u32) 0; diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 6938056a921a..b60ccc34b9c0 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -128,26 +128,18 @@ xdr_decode_fhandle(u32 *p, struct nfs_fh *fh) * nanosecond field. */ static inline u32 * -xdr_encode_time(u32 *p, time_t time) +xdr_encode_time3(u32 *p, struct timespec *timep) { - *p++ = htonl(time); - *p++ = 0; + *p++ = htonl(timep->tv_sec); + *p++ = htonl(timep->tv_nsec); return p; } static inline u32 * -xdr_decode_time3(u32 *p, u64 *timep) +xdr_decode_time3(u32 *p, struct timespec *timep) { - u64 tmp = (u64)ntohl(*p++) << 32; - *timep = tmp + (u64)ntohl(*p++); - return p; -} - -static inline u32 * -xdr_encode_time3(u32 *p, u64 time) -{ - *p++ = htonl(time >> 32); - *p++ = htonl(time & 0xFFFFFFFF); + timep->tv_sec = ntohl(*p++); + timep->tv_nsec = ntohl(*p++); return p; } @@ -212,7 +204,7 @@ xdr_encode_sattr(u32 *p, struct iattr *attr) } if (attr->ia_valid & ATTR_ATIME_SET) { *p++ = xdr_two; - p = xdr_encode_time(p, attr->ia_atime.tv_sec); + p = xdr_encode_time3(p, &attr->ia_atime); } else if (attr->ia_valid & ATTR_ATIME) { *p++ = xdr_one; } else { @@ -220,7 +212,7 @@ xdr_encode_sattr(u32 *p, struct iattr *attr) } if (attr->ia_valid & ATTR_MTIME_SET) { *p++ = xdr_two; - p = xdr_encode_time(p, attr->ia_mtime.tv_sec); + p = xdr_encode_time3(p, &attr->ia_mtime); } else if (attr->ia_valid & ATTR_MTIME) { *p++ = xdr_one; } else { @@ -288,7 +280,7 @@ nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args) p = xdr_encode_sattr(p, args->sattr); *p++ = htonl(args->guard); if (args->guard) - p = xdr_encode_time3(p, args->guardtime); + p = xdr_encode_time3(p, &args->guardtime); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a3cad64cc41e..b17c138681eb 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -563,13 +563,13 @@ nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short por { struct nfs4_setclientid *setclientid = GET_OP(cp, setclientid); struct nfs_server *server = cp->server; - struct timeval tv; + struct timespec tv; u32 *p; - do_gettimeofday(&tv); - p = (u32 *)setclientid->sc_verifier; + tv = CURRENT_TIME; + p = (u32 *)setclientid->sc_verifier; *p++ = tv.tv_sec; - *p++ = tv.tv_usec; + *p++ = tv.tv_nsec; setclientid->sc_name = server->ip_addr; sprintf(setclientid->sc_netid, "udp"); sprintf(setclientid->sc_uaddr, "%s.%d.%d", server->ip_addr, port >> 8, port & 255); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 68d2f765dd33..7b6c503e4cc7 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -873,8 +873,8 @@ xdr_error: \ } while (0) #define READTIME(x) do { \ p++; \ - (x) = (u64)ntohl(*p++) << 32; \ - (x) |= ntohl(*p++); \ + (x.tv_sec) = ntohl(*p++); \ + (x.tv_nsec) = ntohl(*p++); \ } while (0) #define COPYMEM(x,nbytes) do { \ memcpy((x), p, nbytes); \ @@ -1228,19 +1228,19 @@ decode_getattr(struct nfs4_compound *cp, int nfserr, struct nfs4_getattr *getatt READ_BUF(12); len += 12; READTIME(nfp->atime); - dprintk("read_attrs: atime=%d\n", (int)nfp->atime); + dprintk("read_attrs: atime=%ld\n", (long)nfp->atime.tv_sec); } if (bmval1 & FATTR4_WORD1_TIME_METADATA) { READ_BUF(12); len += 12; READTIME(nfp->ctime); - dprintk("read_attrs: ctime=%d\n", (int)nfp->ctime); + dprintk("read_attrs: ctime=%ld\n", (long)nfp->ctime.tv_sec); } if (bmval1 & FATTR4_WORD1_TIME_MODIFY) { READ_BUF(12); len += 12; READTIME(nfp->mtime); - dprintk("read_attrs: mtime=%d\n", (int)nfp->mtime); + dprintk("read_attrs: mtime=%ld\n", (long)nfp->mtime.tv_sec); } if (len != attrlen) goto xdr_error; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index d6f510d638d0..8d15e17c0b94 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -118,8 +118,8 @@ struct nfs_inode { * mtime != read_cache_mtime */ unsigned long read_cache_jiffies; - __u64 read_cache_ctime; - __u64 read_cache_mtime; + struct timespec read_cache_ctime; + struct timespec read_cache_mtime; __u64 read_cache_isize; unsigned long attrtimeo; unsigned long attrtimeo_timestamp; @@ -416,20 +416,6 @@ nfs_fileid_to_ino_t(u64 fileid) return ino; } -static inline time_t -nfs_time_to_secs(__u64 time) -{ - return (time_t)(time >> 32); -} - - -static inline u32 -nfs_time_to_nsecs(__u64 time) -{ - return time & 0xffffffff; -} - - /* NFS root */ extern void * nfs_root_data(void); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 4bb5125056e7..6c82048e2acf 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -6,8 +6,8 @@ struct nfs_fattr { unsigned short valid; /* which fields are valid */ __u64 pre_size; /* pre_op_attr.size */ - __u64 pre_mtime; /* pre_op_attr.mtime */ - __u64 pre_ctime; /* pre_op_attr.ctime */ + struct timespec pre_mtime; /* pre_op_attr.mtime */ + struct timespec pre_ctime; /* pre_op_attr.ctime */ enum nfs_ftype type; /* always use NFSv2 types */ __u32 mode; __u32 nlink; @@ -32,9 +32,9 @@ struct nfs_fattr { } nfs4; } fsid_u; __u64 fileid; - __u64 atime; - __u64 mtime; - __u64 ctime; + struct timespec atime; + struct timespec mtime; + struct timespec ctime; __u64 change_attr; /* NFSv4 change attribute */ __u64 pre_change_attr;/* pre-op NFSv4 change attribute */ unsigned long timestamp; @@ -219,7 +219,7 @@ struct nfs3_sattrargs { struct nfs_fh * fh; struct iattr * sattr; unsigned int guard; - __u64 guardtime; + struct timespec guardtime; }; struct nfs3_diropargs { -- cgit v1.2.3 From 0d3b7ccf5759e4a8a6dec610bbff7952405b5e7e Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 18 Nov 2002 09:46:04 -0600 Subject: kobject - expose backend helpers to registration interface. The interface should now be more sane and protect against races better. kobject_register() was split into two helpers: kobject_init() and kobject_add(). It calls both consecutively, though both are also exposed for use by users that want to use the objects w/o adding them to the object hierarchy. kobject_unregister() was made simply a wrapper for kobject_del() and kobject_put(), which are both also exposed. The guts of kobject_put() was moved into kobject_cleanup(), which it calls when the reference count hits 0. (This was done for clarity). The infrastructure now takes a lot in kobject_get() and kobject_put() when checking and modifying the objects' reference counts. This was an obvious one that hsould have been fixed long ago. kobject_add() increments the refcount of the object, which is decremented when kobject_del() is called. This guarantees that the object's memory cannot be freed if it has been added to the hierarchy, and kobject_del() has not been called on it. kobject_init() is now the function that increments the refcount on the object's subsystem, which is decremented only after its release() method has been called for the object in kobject_cleanup(). The documentation has been updated to reflect these changes. --- Documentation/kobject.txt | 125 +++++++++++++++++++++++++++++++------- include/linux/kobject.h | 4 ++ lib/kobject.c | 152 ++++++++++++++++++++++++++++------------------ 3 files changed, 201 insertions(+), 80 deletions(-) (limited to 'include/linux') diff --git a/Documentation/kobject.txt b/Documentation/kobject.txt index bc4ddfde8158..692467d0255c 100644 --- a/Documentation/kobject.txt +++ b/Documentation/kobject.txt @@ -5,7 +5,9 @@ Patrick Mochel 30 October 2002 -kobjects +1. kobjects + +1.1 Description struct kobject introduces a simple, intregral datatype and a simple set of semantics for operating on the device. kobjects are intended to @@ -13,8 +15,8 @@ be embedded in larger data structures and replace fields it duplicates. A set of library functions has been developed to assist in the manipulation of kobjects. -struct kobject looks like this: +1.2 Defintion struct kobject { char name[16]; @@ -26,21 +28,29 @@ struct kobject { }; void kobject_init(struct kobject *); +int kobject_add(struct kobject *); int kobject_register(struct kobject *); + +void kobject_del(struct kobject *); +void kobject_cleanup(struct kobject *); void kobject_unregister(struct kobject *); + struct kobject * kobject_get(struct kobject *); void kobject_put(struct kobject *); -subsystems +2. subsystems + +2.1 Description struct subsystem is introduced to describe a collection of objects of a certain type. subsystems are kobjects themselves, though they contain lists of kobjects that belong to that subsystem. Objects of a subsystem (the embedder objects in which kobjects live) are all of the -same type. The interface looks like: +same type. +2.2 Definition struct subsystem { struct kobject kobj; @@ -60,6 +70,95 @@ struct subsystem * subsys_get(struct subsystem * s); void subsys_put(struct subsystem * s); +3. The Interface + +The kobject API provides a symmeticral interface that may be used in +one of two ways: by using the default front-end registration +interface, or by directly using the backend helpers the registration +interface uses. + +3.1 Default Usage + +The default usage is to use kobjet_register() to add a device to the +object hierarchy, and kobject_unregister() to remove it. + +kobject_register() will call kobject_init() and kobject_add() +consecutively. kobject_init() will initialize the object and increment +the reference count of the subsystem the object belongs to. It will +leave the reference count of the object at 1. + +kobject_add() will insert it into the object hierarchy and create +a sysfs directory for the object. This will increment the reference +count of the object, leaving it at 2. + +kobject_unregister() will call kobject_del() and kobject_put() +consecutively. kobject_del() will remove the object from the hierarchy +and the sysfs directory for the object. It will decrement the +reference count for the object. Assuming there are no other users of +the object, it will be left at 1. + +kobject_put() will decrement the reference count of the object, and +when it reaches 0, call kobject_cleanup(). This will happen +immediately if there are no other users of the object. +kobject_cleanup() will call the subsystem's release() method +for the object, and decrement the subsystem's reference count. + +Because kobject_unregister() calls kobject_put(), instead of +kobject_cleanup() directly, when an object is unregistered, the +pointer to the object is guaranteed to remain valid until the last +reference to the object has gone away. + +Users of objects should call kobject_get() to obtain a reference to +the object that they are using. If the object passed to it is a valid +object (i.e. still present in the system), it will return a pointer to +the object. Otherwise, it will return NULL. + +When users are done using an object, they should call kobject_put() to +decrement the reference count on the object. As explained above, when +the reference count for the object reaches 0, kobject_cleanup() will +be called for the object. + + +3.2 Backend Usage + +Users of the kobject infrastructure may use the backend functions +directly. In order to maintain consistency and reduce confusion, users +of the interface should use only the front end registration-oriented +interface, or the backend helpers. + +Using the backend helpers allows code to use the kobjects solely for +the reference counting and garbage collection mechanisms, and +optionally adding them to the object hierarchy or exporting them via +sysfs. + +To take advantage of this side of the interface, users should call +kobject_init() to initialize the object. As stated above, this will +leave the reference count of the object at 1, and will enable the +subsystem to use the reference count of the object. + +When the life of the object is ending, the kobject_put() should be +called to decrement the reference count of the object. Just like +above, this will call kobject_cleanup() when the reference count +reaches 0, and release() method of the object's subsystem will be +called. + +During the lifetime of the object, kobject_add() and kobject_del() may +be called to add the object to the hierarchy and export it via +sysfs. kobject_del() must always be called if kobject_add() has +previously been called. Care should be taken to ensure kobject_del() +is called before the final kobject_put() is called, though not doing +so will not cause catastrophe, only confusion when reading the source +code. Fatal results are avoided by having kobject_add() increment the +reference count of the object, for kobject_del() to decrement. + + +3.3 Summary + +Using either interface, users should obtain the same results. The +registration interface does the same actions as the backend interface, +though it guarantees that initialization and addition, and deletion +and cleanup, happen consecutively. + Familial Relations @@ -134,21 +233,3 @@ object that registers with them. A subsystem definition may contain a NULL-terminated array of attributes that will be exported when an object is registered with the subsystem. - -Reference Counting - -All objects contain reference counts. All functions accessing objects -should increment the reference count until they are finished, and -decrement the reference count. When an object is initialized, it -receives a reference count of 1. When a device is unregistered, the -reference is decremented. When the reference counts reaches 0, the -subsystem's ->release() callback for that object type (remember -subsystems control only one type of device each) is called; and the -reference counts of the kobject's subsystem and parent are -decremented. - -The ->release() callback is the opportunity for the subsystem to free -memory allocated for the object. It is the notification that -absolutely no one is using the structure any more (and can't acquire a -reference to it), so it is safe to free it. - diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 40de16d0a227..7b3157a78f7e 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -24,6 +24,10 @@ struct kobject { }; extern void kobject_init(struct kobject *); +extern void kobject_cleanup(struct kobject *); + +extern int kobject_add(struct kobject *); +extern void kobject_del(struct kobject *); extern int kobject_register(struct kobject *); extern void kobject_unregister(struct kobject *); diff --git a/lib/kobject.c b/lib/kobject.c index bc202edb4e3b..33e4f8c9ce58 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -9,8 +9,10 @@ #include #include +static spinlock_t kobj_lock = SPIN_LOCK_UNLOCKED; + /** - * kobject_populate_dir - populate directory with attributes. + * populate_dir - populate directory with attributes. * @kobj: object we're working on. * * Most subsystems have a set of default attributes that @@ -21,7 +23,7 @@ * */ -static int kobject_populate_dir(struct kobject * kobj) +static int populate_dir(struct kobject * kobj) { struct subsystem * s = kobj->subsys; struct attribute * attr; @@ -37,6 +39,20 @@ static int kobject_populate_dir(struct kobject * kobj) return error; } +static int create_dir(struct kobject * kobj) +{ + int error = 0; + if (strlen(kobj->name)) { + error = sysfs_create_dir(kobj); + if (!error) { + if ((error = populate_dir(kobj))) + sysfs_remove_dir(kobj); + } + } + return error; +} + + /** * kobject_init - initialize object. * @kobj: object in question. @@ -46,70 +62,88 @@ void kobject_init(struct kobject * kobj) { atomic_set(&kobj->refcount,1); INIT_LIST_HEAD(&kobj->entry); + kobj->subsys = subsys_get(kobj->subsys); } /** - * kobject_register - register an object. - * @kobj: object in question. - * - * For now, fill in the replicated fields in the object's - * directory entry, and create a dir in sysfs. - * This stuff should go away in the future, as we move - * more implicit things to sysfs. + * kobject_add - add an object to the hierarchy. + * @kobj: object. */ -int kobject_register(struct kobject * kobj) +int kobject_add(struct kobject * kobj) { int error = 0; - struct subsystem * s = subsys_get(kobj->subsys); + struct subsystem * s = kobj->subsys; struct kobject * parent = kobject_get(kobj->parent); - pr_debug("kobject %s: registering\n",kobj->name); - if (parent) - pr_debug(" parent is %s\n",parent->name); + if (!(kobj = kobject_get(kobj))) + return -ENOENT; + pr_debug("kobject %s: registering. parent: %s, subsys: %s\n", + kobj->name, parent ? parent->name : "", + kobj->subsys ? kobj->subsys->kobj.name : "" ); + if (s) { down_write(&s->rwsem); if (parent) list_add_tail(&kobj->entry,&parent->entry); else { list_add_tail(&kobj->entry,&s->list); - kobj->parent = &s->kobj; + kobj->parent = kobject_get(&s->kobj); } up_write(&s->rwsem); } - if (strlen(kobj->name)) { - error = sysfs_create_dir(kobj); - if (!error) { - error = kobject_populate_dir(kobj); - if (error) - sysfs_remove_dir(kobj); - } - } + error = create_dir(kobj); + if (error && kobj->parent) + kobject_put(kobj->parent); return error; } + /** - * kobject_unregister - unlink an object. - * @kobj: object going away. - * - * The device has been told to be removed, but may - * not necessarily be disappearing from the kernel. - * So, we remove the directory and decrement the refcount - * that we set with kobject_register(). - * - * Eventually (maybe now), the refcount will hit 0, and - * put_device() will clean the device up. + * kobject_register - initialize and add an object. + * @kobj: object in question. */ -void kobject_unregister(struct kobject * kobj) +int kobject_register(struct kobject * kobj) +{ + int error = 0; + if (kobj) { + kobject_init(kobj); + error = kobject_add(kobj); + if (error) + kobject_cleanup(kobj); + } else + error = -EINVAL; + return error; +} + +/** + * kobject_del - unlink kobject from hierarchy. + * @kobj: object. + */ + +void kobject_del(struct kobject * kobj) { - pr_debug("kobject %s: unregistering\n",kobj->name); sysfs_remove_dir(kobj); if (kobj->subsys) { down_write(&kobj->subsys->rwsem); list_del_init(&kobj->entry); up_write(&kobj->subsys->rwsem); } + if (kobj->parent) + kobject_put(kobj->parent); + kobject_put(kobj); +} + +/** + * kobject_unregister - remove object from hierarchy and decrement refcount. + * @kobj: object going away. + */ + +void kobject_unregister(struct kobject * kobj) +{ + pr_debug("kobject %s: unregistering\n",kobj->name); + kobject_del(kobj); kobject_put(kobj); } @@ -121,45 +155,48 @@ void kobject_unregister(struct kobject * kobj) struct kobject * kobject_get(struct kobject * kobj) { struct kobject * ret = kobj; + spin_lock(&kobj_lock); if (kobj && atomic_read(&kobj->refcount) > 0) atomic_inc(&kobj->refcount); else ret = NULL; + spin_unlock(&kobj_lock); return ret; } /** - * kobject_put - decrement refcount for object. + * kobject_cleanup - free kobject resources. * @kobj: object. - * - * Decrement the refcount, and check if 0. If it is, then - * we're gonna need to clean it up, and decrement the refcount - * of its parent. - * - * @kobj->parent could point to its subsystem, which we also - * want to decrement the reference count for. We always dec - * the refcount for the parent, but only do so for the subsystem - * if it points to a different place than the parent. */ -void kobject_put(struct kobject * kobj) +void kobject_cleanup(struct kobject * kobj) { - struct kobject * parent = kobj->parent; struct subsystem * s = kobj->subsys; - if (!atomic_dec_and_test(&kobj->refcount)) - return; - pr_debug("kobject %s: cleaning up\n",kobj->name); if (s) { + down_write(&s->rwsem); + list_del_init(&kobj->entry); if (s->release) s->release(kobj); - if (&s->kobj != parent) - subsys_put(s); - } + up_write(&s->rwsem); + subsys_put(s); + } +} + +/** + * kobject_put - decrement refcount for object. + * @kobj: object. + * + * Decrement the refcount, and if 0, call kobject_cleanup(). + */ - if (parent) - kobject_put(parent); +void kobject_put(struct kobject * kobj) +{ + if (!atomic_dec_and_lock(&kobj->refcount, &kobj_lock)) + return; + spin_unlock(&kobj_lock); + kobject_cleanup(kobj); } @@ -180,9 +217,8 @@ int subsystem_register(struct subsystem * s) subsystem_init(s); if (s->parent) s->kobj.parent = &s->parent->kobj; - pr_debug("subsystem %s: registering\n",s->kobj.name); - if (s->parent) - pr_debug(" parent is %s\n",s->parent->kobj.name); + pr_debug("subsystem %s: registering, parent: %s\n", + s->kobj.name,s->parent ? s->parent->kobj.name : ""); return kobject_register(&s->kobj); } -- cgit v1.2.3 From 3c7707e07cff36292f7451a1dee045f752c8f924 Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 18 Nov 2002 08:00:26 -0800 Subject: [CRYPTO]: Add null algorithms and minor cleanups. --- Documentation/crypto/api-intro.txt | 7 +++++++ crypto/Kconfig | 6 ++++++ crypto/Makefile | 1 + crypto/api.c | 4 ++-- crypto/compress.c | 12 ++++++------ include/linux/crypto.h | 10 +++++++--- 6 files changed, 29 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/Documentation/crypto/api-intro.txt b/Documentation/crypto/api-intro.txt index b8cc036cac32..f2e42883bdb5 100644 --- a/Documentation/crypto/api-intro.txt +++ b/Documentation/crypto/api-intro.txt @@ -73,6 +73,13 @@ add the following line to /etc/modules.conf: alias des3_ede des +The Null algorithms reside in the crypto_null module, so these lines +should also be added: + + alias cipher_null crypto_null + alias digest_null crypto_null + alias compress_null crypto_null + DEVELOPER NOTES diff --git a/crypto/Kconfig b/crypto/Kconfig index f09c10bcfe6b..b87a265717d6 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -16,6 +16,12 @@ config CRYPTO_HMAC HMAC: Keyed-Hashing for Message Authentication (RFC2104). This is required for IPSec. +config CRYPTO_NULL + tristate "Null algorithms" + depends on CRYPTO + help + These are 'Null' algorithms, used by IPsec, which do nothing. + config CRYPTO_MD4 tristate "MD4 digest algorithm" depends on CRYPTO diff --git a/crypto/Makefile b/crypto/Makefile index bd8973bdd84d..d94a470b3a9d 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -9,6 +9,7 @@ autoload-crypto-$(CONFIG_KMOD) = autoload.o obj-$(CONFIG_CRYPTO) += api.o cipher.o digest.o compress.o $(autoload-crypto-y) obj-$(CONFIG_CRYPTO_HMAC) += hmac.o +obj-$(CONFIG_CRYPTO_NULL) += crypto_null.o obj-$(CONFIG_CRYPTO_MD4) += md4.o obj-$(CONFIG_CRYPTO_MD5) += md5.o obj-$(CONFIG_CRYPTO_SHA1) += sha1.o diff --git a/crypto/api.c b/crypto/api.c index 7657bd59eefa..0649c6ebb781 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -63,7 +63,7 @@ static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags) case CRYPTO_ALG_TYPE_DIGEST: return crypto_init_digest_flags(tfm, flags); - case CRYPTO_ALG_TYPE_COMP: + case CRYPTO_ALG_TYPE_COMPRESS: return crypto_init_compress_flags(tfm, flags); default: @@ -83,7 +83,7 @@ static int crypto_init_ops(struct crypto_tfm *tfm) case CRYPTO_ALG_TYPE_DIGEST: return crypto_init_digest_ops(tfm); - case CRYPTO_ALG_TYPE_COMP: + case CRYPTO_ALG_TYPE_COMPRESS: return crypto_init_compress_ops(tfm); default: diff --git a/crypto/compress.c b/crypto/compress.c index 773fc5764050..7baaae047941 100644 --- a/crypto/compress.c +++ b/crypto/compress.c @@ -18,15 +18,15 @@ #include #include "internal.h" -/* - * This code currently implements blazingly fast and - * lossless Quadruple ROT13 compression. - */ static void crypto_compress(struct crypto_tfm *tfm) -{ } +{ + tfm->__crt_alg->cra_compress.coa_compress(); +} static void crypto_decompress(struct crypto_tfm *tfm) -{ } +{ + tfm->__crt_alg->cra_compress.coa_decompress(); +} int crypto_init_compress_flags(struct crypto_tfm *tfm, u32 flags) { diff --git a/include/linux/crypto.h b/include/linux/crypto.h index b3448683d02d..960e54133e29 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -29,7 +29,7 @@ #define CRYPTO_ALG_TYPE_MASK 0x000000ff #define CRYPTO_ALG_TYPE_CIPHER 0x00000001 #define CRYPTO_ALG_TYPE_DIGEST 0x00000002 -#define CRYPTO_ALG_TYPE_COMP 0x00000004 +#define CRYPTO_ALG_TYPE_COMPRESS 0x00000004 /* @@ -209,16 +209,19 @@ static inline u32 crypto_tfm_alg_type(struct crypto_tfm *tfm) static inline unsigned int crypto_tfm_alg_min_keysize(struct crypto_tfm *tfm) { + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); return tfm->__crt_alg->cra_cipher.cia_min_keysize; } static inline unsigned int crypto_tfm_alg_max_keysize(struct crypto_tfm *tfm) { + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); return tfm->__crt_alg->cra_cipher.cia_max_keysize; } static inline unsigned int crypto_tfm_alg_ivsize(struct crypto_tfm *tfm) { + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); return tfm->__crt_alg->cra_cipher.cia_ivsize; } @@ -229,6 +232,7 @@ static inline unsigned int crypto_tfm_alg_blocksize(struct crypto_tfm *tfm) static inline unsigned int crypto_tfm_alg_digestsize(struct crypto_tfm *tfm) { + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST); return tfm->__crt_alg->cra_digest.dia_digestsize; } @@ -302,13 +306,13 @@ static inline void crypto_cipher_get_iv(struct crypto_tfm *tfm, static inline void crypto_comp_compress(struct crypto_tfm *tfm) { - BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_COMP); + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_COMPRESS); tfm->crt_compress.cot_compress(tfm); } static inline void crypto_comp_decompress(struct crypto_tfm *tfm) { - BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_COMP); + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_COMPRESS); tfm->crt_compress.cot_decompress(tfm); } -- cgit v1.2.3 From 715feb63c673c5009d439d95fcb228ee92ab0f06 Mon Sep 17 00:00:00 2001 From: Ben Fennema Date: Mon, 18 Nov 2002 17:15:31 -0800 Subject: [PATCH] fix nanosecond stat timefields in UDF --- fs/udf/file.c | 3 -- fs/udf/ialloc.c | 6 ++-- fs/udf/inode.c | 88 ++++++++++++++++++----------------------------- fs/udf/namei.c | 6 ---- fs/udf/super.c | 9 ++--- fs/udf/udf_i.h | 3 -- fs/udf/udfdecl.h | 4 +-- fs/udf/udftime.c | 14 ++++---- include/linux/udf_fs_i.h | 5 +-- include/linux/udf_fs_sb.h | 2 +- 10 files changed, 50 insertions(+), 90 deletions(-) (limited to 'include/linux') diff --git a/fs/udf/file.c b/fs/udf/file.c index 5016ee1dc7e5..4c519798fc1b 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -145,10 +145,7 @@ static ssize_t udf_file_write(struct file * file, const char * buf, retval = generic_file_write(file, buf, count, ppos); if (retval > 0) - { - UDF_I_UCTIME(inode) = UDF_I_UMTIME(inode) = CURRENT_UTIME; mark_inode_dirty(inode); - } return retval; } diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c index 35ffa13420d6..be11d8af3b7c 100644 --- a/fs/udf/ialloc.c +++ b/fs/udf/ialloc.c @@ -156,10 +156,8 @@ struct inode * udf_new_inode (struct inode *dir, int mode, int * err) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; else UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - UDF_I_CRTIME(inode) = get_seconds(); - UDF_I_UMTIME(inode) = UDF_I_UCTIME(inode) = - UDF_I_UCRTIME(inode) = get_seconds(); + inode->i_mtime = inode->i_atime = inode->i_ctime = + UDF_I_CRTIME(inode) = CURRENT_TIME; insert_inode_hash(inode); mark_inode_dirty(inode); diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 9ce72a9c9136..d1047e8aa65c 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -573,7 +573,6 @@ static struct buffer_head * inode_getblk(struct inode * inode, long block, UDF_I_NEXT_ALLOC_BLOCK(inode) = block; UDF_I_NEXT_ALLOC_GOAL(inode) = newblocknum; inode->i_ctime = CURRENT_TIME; - UDF_I_UCTIME(inode) = CURRENT_UTIME; if (IS_SYNC(inode)) udf_sync_inode(inode); @@ -877,7 +876,6 @@ void udf_truncate(struct inode * inode) } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - UDF_I_UMTIME(inode) = UDF_I_UCTIME(inode) = CURRENT_UTIME; if (IS_SYNC(inode)) udf_sync_inode (inode); else @@ -1023,10 +1021,6 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) UDF_I_STRAT4096(inode) = 1; UDF_I_ALLOCTYPE(inode) = le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK; - UDF_I_UMTIME(inode) = 0; - UDF_I_UCTIME(inode) = 0; - UDF_I_CRTIME(inode) = 0; - UDF_I_UCRTIME(inode) = 0; UDF_I_UNIQUE(inode) = 0; UDF_I_LENEATTR(inode) = 0; UDF_I_LENEXTENTS(inode) = 0; @@ -1084,40 +1078,33 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) lets_to_cpu(fe->accessTime)) ) { inode->i_atime.tv_sec = convtime; - inode->i_atime.tv_nsec = 0; + inode->i_atime.tv_nsec = convtime_usec * 1000; } else { - inode->i_atime.tv_sec = UDF_SB_RECORDTIME(inode->i_sb); - inode->i_atime.tv_nsec = 0; + inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(fe->modificationTime)) ) { inode->i_mtime.tv_sec = convtime; - inode->i_mtime.tv_nsec = 0; - UDF_I_UMTIME(inode) = convtime_usec; + inode->i_mtime.tv_nsec = convtime_usec * 1000; } else { - inode->i_mtime.tv_sec = UDF_SB_RECORDTIME(inode->i_sb); - inode->i_mtime.tv_nsec = 0; - UDF_I_UMTIME(inode) = 0; + inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(fe->attrTime)) ) { inode->i_ctime.tv_sec = convtime; - inode->i_ctime.tv_nsec = 0; - UDF_I_UCTIME(inode) = convtime_usec; + inode->i_ctime.tv_nsec = convtime_usec * 1000; } else { - inode->i_ctime.tv_sec = UDF_SB_RECORDTIME(inode->i_sb); - inode->i_ctime.tv_nsec = 0; - UDF_I_UCTIME(inode) = 0; + inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); } UDF_I_UNIQUE(inode) = le64_to_cpu(fe->uniqueID); @@ -1135,52 +1122,44 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) lets_to_cpu(efe->accessTime)) ) { inode->i_atime.tv_sec = convtime; - inode->i_atime.tv_nsec = 0; + inode->i_atime.tv_nsec = convtime_usec * 1000; } else { - inode->i_atime.tv_sec = UDF_SB_RECORDTIME(inode->i_sb); - inode->i_atime.tv_nsec = 0; + inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->modificationTime)) ) { inode->i_mtime.tv_sec = convtime; - inode->i_mtime.tv_nsec = 0; - UDF_I_UMTIME(inode) = convtime_usec; + inode->i_mtime.tv_nsec = convtime_usec * 1000; } else { - inode->i_mtime.tv_sec = UDF_SB_RECORDTIME(inode->i_sb); - inode->i_mtime.tv_nsec = 0; - UDF_I_UMTIME(inode) = 0; + inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->createTime)) ) { - UDF_I_CRTIME(inode) = convtime; - UDF_I_UCRTIME(inode) = convtime_usec; + UDF_I_CRTIME(inode).tv_sec = convtime; + UDF_I_CRTIME(inode).tv_nsec = convtime_usec * 1000; } else { UDF_I_CRTIME(inode) = UDF_SB_RECORDTIME(inode->i_sb); - UDF_I_UCRTIME(inode) = 0; } if ( udf_stamp_to_time(&convtime, &convtime_usec, lets_to_cpu(efe->attrTime)) ) { inode->i_ctime.tv_sec = convtime; - inode->i_ctime.tv_nsec = 0; - UDF_I_UCTIME(inode) = convtime_usec; + inode->i_ctime.tv_nsec = convtime_usec * 1000; } else { - inode->i_ctime.tv_sec = UDF_SB_RECORDTIME(inode->i_sb); - inode->i_ctime.tv_nsec = 0; - UDF_I_UCTIME(inode) = 0; + inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); } UDF_I_UNIQUE(inode) = le64_to_cpu(efe->uniqueID); @@ -1423,11 +1402,11 @@ udf_update_inode(struct inode *inode, int do_sync) (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> (inode->i_sb->s_blocksize_bits - 9)); - if (udf_time_to_stamp(&cpu_time, inode->i_atime.tv_sec, 0)) + if (udf_time_to_stamp(&cpu_time, inode->i_atime)) fe->accessTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, inode->i_mtime.tv_sec, UDF_I_UMTIME(inode))) + if (udf_time_to_stamp(&cpu_time, inode->i_mtime)) fe->modificationTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, inode->i_ctime.tv_sec, UDF_I_UCTIME(inode))) + if (udf_time_to_stamp(&cpu_time, inode->i_ctime)) fe->attrTime = cpu_to_lets(cpu_time); memset(&(fe->impIdent), 0, sizeof(regid)); strcpy(fe->impIdent.ident, UDF_ID_DEVELOPER); @@ -1447,33 +1426,32 @@ udf_update_inode(struct inode *inode, int do_sync) (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> (inode->i_sb->s_blocksize_bits - 9)); - if (UDF_I_CRTIME(inode) >= inode->i_atime.tv_sec) + if (UDF_I_CRTIME(inode).tv_sec > inode->i_atime.tv_sec || + (UDF_I_CRTIME(inode).tv_sec == inode->i_atime.tv_sec && + UDF_I_CRTIME(inode).tv_nsec > inode->i_atime.tv_nsec)) { - UDF_I_CRTIME(inode) = inode->i_atime.tv_sec; - UDF_I_UCRTIME(inode) = 0; + UDF_I_CRTIME(inode) = inode->i_atime; } - if (UDF_I_CRTIME(inode) > inode->i_mtime.tv_sec || - (UDF_I_CRTIME(inode) == inode->i_mtime.tv_sec && - UDF_I_UCRTIME(inode) > UDF_I_UMTIME(inode))) + if (UDF_I_CRTIME(inode).tv_sec > inode->i_mtime.tv_sec || + (UDF_I_CRTIME(inode).tv_sec == inode->i_mtime.tv_sec && + UDF_I_CRTIME(inode).tv_nsec > inode->i_mtime.tv_nsec)) { - UDF_I_CRTIME(inode) = inode->i_mtime.tv_sec; - UDF_I_UCRTIME(inode) = UDF_I_UMTIME(inode); + UDF_I_CRTIME(inode) = inode->i_mtime; } - if (UDF_I_CRTIME(inode) > inode->i_ctime.tv_sec || - (UDF_I_CRTIME(inode) == inode->i_ctime.tv_sec && - UDF_I_UCRTIME(inode) > UDF_I_UCTIME(inode))) + if (UDF_I_CRTIME(inode).tv_sec > inode->i_ctime.tv_sec || + (UDF_I_CRTIME(inode).tv_sec == inode->i_ctime.tv_sec && + UDF_I_CRTIME(inode).tv_nsec > inode->i_ctime.tv_nsec)) { - UDF_I_CRTIME(inode) = inode->i_ctime.tv_sec; - UDF_I_UCRTIME(inode) = UDF_I_UCTIME(inode); + UDF_I_CRTIME(inode) = inode->i_ctime; } - if (udf_time_to_stamp(&cpu_time, inode->i_atime.tv_sec, 0)) + if (udf_time_to_stamp(&cpu_time, inode->i_atime)) efe->accessTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, inode->i_mtime.tv_sec, UDF_I_UMTIME(inode))) + if (udf_time_to_stamp(&cpu_time, inode->i_mtime)) efe->modificationTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, UDF_I_CRTIME(inode), UDF_I_UCRTIME(inode))) + if (udf_time_to_stamp(&cpu_time, UDF_I_CRTIME(inode))) efe->createTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, inode->i_ctime.tv_sec, UDF_I_UCTIME(inode))) + if (udf_time_to_stamp(&cpu_time, inode->i_ctime)) efe->attrTime = cpu_to_lets(cpu_time); memset(&(efe->impIdent), 0, sizeof(regid)); diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 84d4c1fc109c..138b1c8e3deb 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -882,7 +882,6 @@ static int udf_rmdir(struct inode * dir, struct dentry * dentry) mark_inode_dirty(inode); dir->i_nlink --; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - UDF_I_UCTIME(inode) = UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME; mark_inode_dirty(dir); end_rmdir: @@ -926,7 +925,6 @@ static int udf_unlink(struct inode * dir, struct dentry * dentry) if (retval) goto end_unlink; dir->i_ctime = dir->i_mtime = CURRENT_TIME; - UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME; mark_inode_dirty(dir); inode->i_nlink--; mark_inode_dirty(inode); @@ -1157,7 +1155,6 @@ static int udf_link(struct dentry * old_dentry, struct inode * dir, udf_release_data(fibh.sbh); inode->i_nlink ++; inode->i_ctime = CURRENT_TIME; - UDF_I_UCTIME(inode) = CURRENT_UTIME; mark_inode_dirty(inode); atomic_inc(&inode->i_count); d_instantiate(dentry, inode); @@ -1251,7 +1248,6 @@ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, * rename. */ old_inode->i_ctime = CURRENT_TIME; - UDF_I_UCTIME(old_inode) = CURRENT_UTIME; mark_inode_dirty(old_inode); /* @@ -1270,11 +1266,9 @@ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; - UDF_I_UCTIME(new_inode) = CURRENT_UTIME; mark_inode_dirty(new_inode); } old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - UDF_I_UCTIME(old_dir) = UDF_I_UMTIME(old_dir) = CURRENT_UTIME; mark_inode_dirty(old_dir); if (dir_fi) diff --git a/fs/udf/super.c b/fs/udf/super.c index 5188e5f0fcf4..a602beb96b53 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -800,7 +800,8 @@ udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) udf_debug("recording time %ld/%ld, %04u/%02u/%02u %02u:%02u (%x)\n", recording, recording_usec, ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone); - UDF_SB_RECORDTIME(sb) = recording; + UDF_SB_RECORDTIME(sb).tv_sec = recording; + UDF_SB_RECORDTIME(sb).tv_nsec = recording_usec * 1000; } if ( !udf_build_ustr(&instr, pvoldesc->volIdent, 32) ) @@ -1339,7 +1340,7 @@ static void udf_open_lvid(struct super_block *sb) UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; - if (udf_time_to_stamp(&cpu_time, get_seconds(), CURRENT_UTIME)) + if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); UDF_SB_LVID(sb)->integrityType = LVID_INTEGRITY_TYPE_OPEN; @@ -1367,7 +1368,7 @@ static void udf_close_lvid(struct super_block *sb) UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; - if (udf_time_to_stamp(&cpu_time, get_seconds(), CURRENT_UTIME)) + if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); if (UDF_MAX_WRITE_VERSION > le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev)) UDF_SB_LVIDIU(sb)->maxUDFWriteRev = cpu_to_le16(UDF_MAX_WRITE_VERSION); @@ -1536,7 +1537,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) if (!silent) { timestamp ts; - udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb), 0); + udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb)); udf_info("UDF %s (%s) Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n", UDFFS_VERSION, UDFFS_DATE, UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute, diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h index 5d87e85e6062..d7dbe6f3ba0c 100644 --- a/fs/udf/udf_i.h +++ b/fs/udf/udf_i.h @@ -18,10 +18,7 @@ static inline struct udf_inode_info *UDF_I(struct inode *inode) #define UDF_I_STRAT4096(X) ( UDF_I(X)->i_strat4096 ) #define UDF_I_NEXT_ALLOC_BLOCK(X) ( UDF_I(X)->i_next_alloc_block ) #define UDF_I_NEXT_ALLOC_GOAL(X) ( UDF_I(X)->i_next_alloc_goal ) -#define UDF_I_UMTIME(X) ( UDF_I(X)->i_umtime ) -#define UDF_I_UCTIME(X) ( UDF_I(X)->i_uctime ) #define UDF_I_CRTIME(X) ( UDF_I(X)->i_crtime ) -#define UDF_I_UCRTIME(X) ( UDF_I(X)->i_ucrtime ) #define UDF_I_SAD(X) ( UDF_I(X)->i_ext.i_sad ) #define UDF_I_LAD(X) ( UDF_I(X)->i_ext.i_lad ) #define UDF_I_DATA(X) ( UDF_I(X)->i_ext.i_data ) diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index a41c46ad9726..8f8db132fdfc 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -28,8 +28,6 @@ #define UDF_NAME_LEN 255 #define UDF_PATH_LEN 1023 -#define CURRENT_UTIME (xtime.tv_nsec / 1000) - #define udf_file_entry_alloc_offset(inode)\ (UDF_I_USE(inode) ?\ sizeof(struct unallocSpaceEntry) :\ @@ -185,6 +183,6 @@ extern uint16_t udf_crc(uint8_t *, uint32_t, uint16_t); /* udftime.c */ extern time_t *udf_stamp_to_time(time_t *, long *, timestamp); -extern timestamp *udf_time_to_stamp(timestamp *, time_t, long); +extern timestamp *udf_time_to_stamp(timestamp *, struct timespec); #endif /* __UDF_DECL_H */ diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c index decaede92657..3722d7d38e4b 100644 --- a/fs/udf/udftime.c +++ b/fs/udf/udftime.c @@ -121,7 +121,7 @@ udf_stamp_to_time(time_t *dest, long *dest_usec, timestamp src) timestamp * -udf_time_to_stamp(timestamp *dest, time_t tv_sec, long tv_usec) +udf_time_to_stamp(timestamp *dest, struct timespec ts) { long int days, rem, y; const unsigned short int *ip; @@ -134,9 +134,9 @@ udf_time_to_stamp(timestamp *dest, time_t tv_sec, long tv_usec) dest->typeAndTimezone = 0x1000 | (offset & 0x0FFF); - tv_sec += offset * 60; - days = tv_sec / SECS_PER_DAY; - rem = tv_sec % SECS_PER_DAY; + ts.tv_sec += offset * 60; + days = ts.tv_sec / SECS_PER_DAY; + rem = ts.tv_sec % SECS_PER_DAY; dest->hour = rem / SECS_PER_HOUR; rem %= SECS_PER_HOUR; dest->minute = rem / 60; @@ -164,9 +164,9 @@ udf_time_to_stamp(timestamp *dest, time_t tv_sec, long tv_usec) dest->month = y + 1; dest->day = days + 1; - dest->centiseconds = tv_usec / 10000; - dest->hundredsOfMicroseconds = (tv_usec - dest->centiseconds * 10000) / 100; - dest->microseconds = (tv_usec - dest->centiseconds * 10000 - + dest->centiseconds = ts.tv_nsec / 10000000; + dest->hundredsOfMicroseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000) / 100; + dest->microseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000 - dest->hundredsOfMicroseconds * 100); return dest; } diff --git a/include/linux/udf_fs_i.h b/include/linux/udf_fs_i.h index f297d528aded..186a5a47b50b 100644 --- a/include/linux/udf_fs_i.h +++ b/include/linux/udf_fs_i.h @@ -43,10 +43,7 @@ typedef struct struct udf_inode_info { - long i_umtime; - long i_uctime; - long i_crtime; - long i_ucrtime; + struct timespec i_crtime; /* Physical address of inode */ lb_addr i_location; __u64 i_unique; diff --git a/include/linux/udf_fs_sb.h b/include/linux/udf_fs_sb.h index e898136bf947..b73cebba5551 100644 --- a/include/linux/udf_fs_sb.h +++ b/include/linux/udf_fs_sb.h @@ -97,7 +97,7 @@ struct udf_sb_info uid_t s_uid; /* Root Info */ - time_t s_recordtime; + struct timespec s_recordtime; /* Fileset Info */ __u16 s_serialnum; -- cgit v1.2.3 From 274c7da79ca2c92a790622b679622c984b75998b Mon Sep 17 00:00:00 2001 From: "Andries E. Brouwer" Date: Mon, 18 Nov 2002 17:25:17 -0800 Subject: [PATCH] compilation fix (tpyo) --- include/linux/device.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index 6ce7d11cc8f9..b434f2725c23 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -438,9 +438,9 @@ extern void device_resume(u32 level); extern void device_shutdown(void); -/* drivrs/base/firmware.c */ +/* drivers/base/firmware.c */ extern int firmware_register(struct subsystem *); -extern void firmware_uregister(struct subsystem *); +extern void firmware_unregister(struct subsystem *); /* debugging and troubleshooting/diagnostic helpers. */ #ifdef DEBUG -- cgit v1.2.3 From 7e6eed164d6d28a5bfabc7e06b0c34e7e367b836 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 19 Nov 2002 04:30:40 -0800 Subject: PCI: removed pcibios_read_config_* and pcibios_write_config_* functions. --- drivers/pci/Makefile | 4 ++-- drivers/pci/compat.c | 37 ------------------------------------- include/linux/pci.h | 17 ----------------- 3 files changed, 2 insertions(+), 56 deletions(-) delete mode 100644 drivers/pci/compat.c (limited to 'include/linux') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 39322fec9a8f..2749c3ccd859 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -3,10 +3,10 @@ # export-objs := access.o hotplug.o pci-driver.o pci.o pool.o \ - probe.o proc.o search.o compat.o setup-bus.o + probe.o proc.o search.o setup-bus.o obj-y += access.o probe.o pci.o pool.o quirks.o \ - compat.o names.o pci-driver.o search.o hotplug.o + names.o pci-driver.o search.o hotplug.o obj-$(CONFIG_PM) += power.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/drivers/pci/compat.c b/drivers/pci/compat.c deleted file mode 100644 index 048c610a9e62..000000000000 --- a/drivers/pci/compat.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * $Id: compat.c,v 1.1 1998/02/16 10:35:50 mj Exp $ - * - * PCI Bus Services -- Function For Backward Compatibility - * - * Copyright 1998--2000 Martin Mares - */ - -#include -#include -#include -#include - -/* Obsolete functions, these will be going away... */ - -#define PCI_OP(rw,size,type) \ -int pcibios_##rw##_config_##size (unsigned char bus, unsigned char dev_fn, \ - unsigned char where, unsigned type val) \ -{ \ - struct pci_dev *dev = pci_find_slot(bus, dev_fn); \ - if (!dev) return PCIBIOS_DEVICE_NOT_FOUND; \ - return pci_##rw##_config_##size(dev, where, val); \ -} - -PCI_OP(read, byte, char *) -PCI_OP(read, word, short *) -PCI_OP(read, dword, int *) -PCI_OP(write, byte, char) -PCI_OP(write, word, short) -PCI_OP(write, dword, int) - -EXPORT_SYMBOL(pcibios_read_config_byte); -EXPORT_SYMBOL(pcibios_read_config_word); -EXPORT_SYMBOL(pcibios_read_config_dword); -EXPORT_SYMBOL(pcibios_write_config_byte); -EXPORT_SYMBOL(pcibios_write_config_word); -EXPORT_SYMBOL(pcibios_write_config_dword); diff --git a/include/linux/pci.h b/include/linux/pci.h index 7206acd7870b..3fa04d5f93f8 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -517,21 +517,6 @@ void pcibios_update_resource(struct pci_dev *, struct resource *, void pcibios_update_irq(struct pci_dev *, int irq); void pcibios_fixup_pbus_ranges(struct pci_bus *, struct pbus_set_ranges_data *); -/* Backward compatibility, don't use in new code! */ - -int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned char *val); -int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned short *val); -int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned int *val); -int pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned char val); -int pcibios_write_config_word (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned short val); -int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned int val); - /* Generic PCI functions used internally */ int pci_bus_exists(const struct list_head *list, int nr); @@ -668,8 +653,6 @@ extern struct pci_dev *isa_bridge; static inline int pci_present(void) { return 0; } #define _PCI_NOP(o,s,t) \ - static inline int pcibios_##o##_config_##s (u8 bus, u8 dfn, u8 where, t val) \ - { return PCIBIOS_FUNC_NOT_SUPPORTED; } \ static inline int pci_##o##_config_##s (struct pci_dev *dev, int where, t val) \ { return PCIBIOS_FUNC_NOT_SUPPORTED; } #define _PCI_NOP_ALL(o,x) _PCI_NOP(o,byte,u8 x) \ -- cgit v1.2.3 From 3dda41af8cf941d5f5db01dd85df502c5d96aa02 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 19 Nov 2002 07:13:45 -0600 Subject: fix queue run on returning I/O [axboe@suse.de] On returning I/O, need to unplug the queue before we call the queue_fn. This fixes a problem in 2.5.48 where the aic7xxx driver hangs under e2fsck. --- drivers/block/ll_rw_blk.c | 11 +++++++++++ drivers/scsi/scsi_error.c | 4 +--- drivers/scsi/scsi_lib.c | 11 +++-------- include/linux/blkdev.h | 1 + 4 files changed, 16 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 4e709cfffd67..336f0eb49cd8 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -1037,6 +1037,16 @@ void blk_stop_queue(request_queue_t *q) spin_unlock_irqrestore(q->queue_lock, flags); } +/** + * blk_run_queue - run a single device queue + * @q The queue to run + */ +void __blk_run_queue(request_queue_t *q) +{ + blk_remove_plug(q); + q->request_fn(q); +} + /** * blk_run_queues - fire all plugged queues * @@ -2198,4 +2208,5 @@ EXPORT_SYMBOL(blk_queue_invalidate_tags); EXPORT_SYMBOL(blk_start_queue); EXPORT_SYMBOL(blk_stop_queue); EXPORT_SYMBOL(__blk_stop_queue); +EXPORT_SYMBOL(__blk_run_queue); EXPORT_SYMBOL(blk_run_queues); diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 88ca4b308774..510a53260bc0 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -1479,8 +1479,6 @@ static void scsi_restart_operations(struct Scsi_Host *shost) */ spin_lock_irqsave(shost->host_lock, flags); for (sdev = shost->host_queue; sdev; sdev = sdev->next) { - request_queue_t *q = &sdev->request_queue; - if ((shost->can_queue > 0 && (shost->host_busy >= shost->can_queue)) || (shost->host_blocked) @@ -1488,7 +1486,7 @@ static void scsi_restart_operations(struct Scsi_Host *shost) break; } - q->request_fn(q); + __blk_run_queue(&sdev->request_queue); } spin_unlock_irqrestore(shost->host_lock, flags); } diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 2098d8766e95..7d4ef17f3cc2 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -227,7 +227,7 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt) /* * Just hit the requeue function for the queue. */ - q->request_fn(q); + __blk_run_queue(q); SDpnt = (Scsi_Device *) q->queuedata; SHpnt = SDpnt->host; @@ -240,8 +240,6 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt) * use function pointers to pick the right one. */ if (SDpnt->single_lun && blk_queue_empty(q) && SDpnt->device_busy ==0) { - request_queue_t *q; - for (SDpnt = SHpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { if (((SHpnt->can_queue > 0) && (SHpnt->host_busy >= SHpnt->can_queue)) @@ -251,8 +249,7 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt) break; } - q = &SDpnt->request_queue; - q->request_fn(q); + __blk_run_queue(&SDpnt->request_queue); } } @@ -267,7 +264,6 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt) all_clear = 1; if (SHpnt->some_device_starved) { for (SDpnt = SHpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { - request_queue_t *q; if ((SHpnt->can_queue > 0 && (SHpnt->host_busy >= SHpnt->can_queue)) || (SHpnt->host_blocked) || (SHpnt->host_self_blocked)) { @@ -276,8 +272,7 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt) if (SDpnt->device_blocked || !SDpnt->starved) { continue; } - q = &SDpnt->request_queue; - q->request_fn(q); + __blk_run_queue(&SDpnt->request_queue); all_clear = 0; } if (SDpnt == NULL && all_clear) { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d9b416ebc966..6730edd230e0 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -321,6 +321,7 @@ extern int scsi_cmd_ioctl(struct block_device *, unsigned int, unsigned long); extern void blk_start_queue(request_queue_t *q); extern void blk_stop_queue(request_queue_t *q); extern void __blk_stop_queue(request_queue_t *q); +extern void __blk_run_queue(request_queue_t *q); static inline request_queue_t *bdev_get_queue(struct block_device *bdev) { -- cgit v1.2.3 From b12cf4fff95fc6bab2b350160cba008c1467d19e Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 19 Nov 2002 08:42:26 -0600 Subject: driver model: exploit kobject contstructs. This makes the driver model core (for devices) exploit the kobject infrastructure more and make the resulting code quite a bit simpler. For one, device_register() mimmicks kobject_register() in that it now only calls device_initialize() and device_add() back to back. Similarly, device_unregister() calls device_del() and put_device() consecutively. device_del() no longer removes and frees the device, it only removes them. It also removes the devices from the global and sibling lists. This was previously done by device_put(), but moved here to be symmetrical with device_add(). The device's parent is now only incremented in device_add() and decremented in device_del(), fixing a bug in which the parent's refcount was incremented twice. Because of these simplifications, the core can easily be converted to use the kobject reference counting infrastructure. get_device() now simply forwards the call to kobject_get() and ditto for put_device(). device_release() is implemented to handle the freeing of devices once their reference count reaches 0. Since we're using the kobject refcounting model, we no longer need the checking or setting of the device state field, so it has been removed. The only users of it were the power routines. In those, it is implicit that we have a valid device, since we've already taken device_sem, and we're walking the list (all modifications are protected by device_sem). struct device::lock, and the helpers to lock/unlock have been removed. No one has ever used them, and no one is likely to use them. --- drivers/base/core.c | 233 +++++++++++++++++++++++++++++++------------------ drivers/base/power.c | 6 +- include/linux/device.h | 25 ------ 3 files changed, 149 insertions(+), 115 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index b6eb63fd9fdc..e591edef8e1d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -69,15 +69,42 @@ static struct sysfs_ops dev_sysfs_ops = { .store = dev_attr_store, }; + +/** + * device_release - free device structure. + * @kobj: device's kobject. + * + * This is called once the reference count for the object + * reaches 0. We forward the call to the device's release + * method, which should handle actually freeing the structure. + */ +static void device_release(struct kobject * kobj) +{ + struct device * dev = to_dev(kobj); + if (dev->release) + dev->release(dev); +} + + +/** + * device_subsys - structure to be registered with kobject core. + */ struct subsystem device_subsys = { .kobj = { .name = "devices", }, + .release = device_release, .sysfs_ops = &dev_sysfs_ops, .default_attrs = dev_default_attrs, }; +/** + * device_create_file - create sysfs attribute file for device. + * @dev: device. + * @attr: device attribute descriptor. + */ + int device_create_file(struct device * dev, struct device_attribute * attr) { int error = 0; @@ -88,6 +115,12 @@ int device_create_file(struct device * dev, struct device_attribute * attr) return error; } +/** + * device_remove_file - remove sysfs attribute file. + * @dev: device. + * @attr: device attribute descriptor. + */ + void device_remove_file(struct device * dev, struct device_attribute * attr) { if (get_device(dev)) { @@ -96,32 +129,72 @@ void device_remove_file(struct device * dev, struct device_attribute * attr) } } + +/** + * device_initialize - init device structure. + * @dev: device. + * + * This prepares the device for use by other layers, + * including adding it to the device hierarchy. + * It is the first half of device_register(), if called by + * that, though it can also be called separately, so one + * may use @dev's fields (e.g. the refcount). + */ + +void device_initialize(struct device *dev) +{ + kobject_init(&dev->kobj); + INIT_LIST_HEAD(&dev->node); + INIT_LIST_HEAD(&dev->children); + INIT_LIST_HEAD(&dev->g_list); + INIT_LIST_HEAD(&dev->driver_list); + INIT_LIST_HEAD(&dev->bus_list); + INIT_LIST_HEAD(&dev->intf_list); +// spin_lock_init(&dev->lock); +} + +/** + * device_add - add device to device hierarchy. + * @dev: device. + * + * This is part 2 of device_register(), though may be called + * separately _iff_ device_initialize() has been called separately. + * + * This adds it to the kobject hierarchy via kobject_add(), adds it + * to the global and sibling lists for the device, then + * adds it to the other relevant subsystems of the driver model. + */ int device_add(struct device *dev) { + struct device * parent; int error; - + if (!dev || !strlen(dev->bus_id)) return -EINVAL; - down(&device_sem); - dev->state = DEVICE_REGISTERED; - if (dev->parent) { - list_add_tail(&dev->g_list,&dev->parent->g_list); - list_add_tail(&dev->node,&dev->parent->children); - } else - list_add_tail(&dev->g_list,&global_device_list); - up(&device_sem); + parent = get_device(dev->parent); pr_debug("DEV: registering device: ID = '%s', name = %s\n", dev->bus_id, dev->name); + /* first, register with generic layer. */ strncpy(dev->kobj.name,dev->bus_id,KOBJ_NAME_LEN); - if (dev->parent) - dev->kobj.parent = &dev->parent->kobj; dev->kobj.subsys = &device_subsys; - if ((error = kobject_register(&dev->kobj))) + if (parent) + dev->kobj.parent = &parent->kobj; + + if ((error = kobject_add(&dev->kobj))) goto register_done; + /* now take care of our own registration */ + down(&device_sem); + if (parent) { + list_add_tail(&dev->g_list,&dev->parent->g_list); + list_add_tail(&dev->node,&parent->children); + } else + list_add_tail(&dev->g_list,&global_device_list); + up(&device_sem); + bus_add_device(dev); /* notify platform of device entry */ @@ -133,95 +206,79 @@ int device_add(struct device *dev) devclass_add_device(dev); register_done: - if (error) { - down(&device_sem); - list_del_init(&dev->g_list); - list_del_init(&dev->node); - up(&device_sem); - } + if (error && parent) + put_device(parent); return error; } -void device_initialize(struct device *dev) -{ - kobject_init(&dev->kobj); - INIT_LIST_HEAD(&dev->node); - INIT_LIST_HEAD(&dev->children); - INIT_LIST_HEAD(&dev->g_list); - INIT_LIST_HEAD(&dev->driver_list); - INIT_LIST_HEAD(&dev->bus_list); - INIT_LIST_HEAD(&dev->intf_list); - spin_lock_init(&dev->lock); - atomic_set(&dev->refcount,1); - dev->state = DEVICE_INITIALIZED; - if (dev->parent) - get_device(dev->parent); -} /** - * device_register - register a device - * @dev: pointer to the device structure - * - * First, make sure that the device has a parent, create - * a directory for it, then add it to the parent's list of - * children. + * device_register - register a device with the system. + * @dev: pointer to the device structure * - * Maintains a global list of all devices, in depth-first ordering. - * The head for that list is device_root.g_list. + * This happens in two clean steps - initialize the device + * and add it to the system. The two steps can be called + * separately, but this is the easiest and most common. + * I.e. you should only call the two helpers separately if + * have a clearly defined need to use and refcount the device + * before it is added to the hierarchy. */ + int device_register(struct device *dev) { - int error; - - if (!dev || !strlen(dev->bus_id)) - return -EINVAL; - device_initialize(dev); - if (dev->parent) - get_device(dev->parent); - error = device_add(dev); - if (error && dev->parent) - put_device(dev->parent); - return error; + return device_add(dev); } + +/** + * get_device - increment reference count for device. + * @dev: device. + * + * This simply forwards the call to kobject_get(), though + * we do take care to provide for the case that we get a NULL + * pointer passed in. + */ + struct device * get_device(struct device * dev) { - struct device * ret = dev; - down(&device_sem); - if (device_present(dev) && atomic_read(&dev->refcount) > 0) - atomic_inc(&dev->refcount); - else - ret = NULL; - up(&device_sem); - return ret; + return dev ? to_dev(kobject_get(&dev->kobj)) : NULL; } + /** - * put_device - decrement reference count, and clean up when it hits 0 - * @dev: device in question + * put_device - decrement reference count. + * @dev: device in question. */ void put_device(struct device * dev) { - down(&device_sem); - if (!atomic_dec_and_test(&dev->refcount)) { - up(&device_sem); - return; - } - list_del_init(&dev->node); - list_del_init(&dev->g_list); - up(&device_sem); + kobject_put(&dev->kobj); +} - WARN_ON(dev->state == DEVICE_REGISTERED); - if (dev->state == DEVICE_GONE) - device_del(dev); -} +/** + * device_del - delete device from system. + * @dev: device. + * + * This is the first part of the device unregistration + * sequence. This removes the device from the lists we control + * from here, has it removed from the other driver model + * subsystems it was added to in device_add(), and removes it + * from the kobject hierarchy. + * + * NOTE: this should be called manually _iff_ device_add() was + * also called manually. + */ void device_del(struct device * dev) { struct device * parent = dev->parent; + down(&device_sem); + list_del_init(&dev->node); + list_del_init(&dev->g_list); + up(&device_sem); + /* Notify the platform of the removal, in case they * need to do anything... */ @@ -233,31 +290,29 @@ void device_del(struct device * dev) bus_remove_device(dev); - if (dev->release) - dev->release(dev); + kobject_del(&dev->kobj); if (parent) put_device(parent); + } /** - * device_unregister - unlink device - * @dev: device going away + * device_unregister - unregister device from system. + * @dev: device going away. * - * The device has been removed from the system, so we disavow knowledge - * of it. It might not be the final reference to the device, so we mark - * it as !present, so no more references to it can be acquired. - * In the end, we decrement the final reference count for it. + * We do this in two parts, like we do device_register(). First, + * we remove it from all the subsystems with device_del(), then + * we decrement the reference count via put_device(). If that + * is the final reference count, the device will be cleaned up + * via device_release() above. Otherwise, the structure will + * stick around until the final reference to the device is dropped. */ void device_unregister(struct device * dev) { - down(&device_sem); - dev->state = DEVICE_GONE; - up(&device_sem); - pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n", dev->bus_id,dev->name); - kobject_unregister(&dev->kobj); + device_del(dev); put_device(dev); } @@ -268,7 +323,11 @@ static int __init device_subsys_init(void) core_initcall(device_subsys_init); +EXPORT_SYMBOL(device_initialize); +EXPORT_SYMBOL(device_add); EXPORT_SYMBOL(device_register); + +EXPORT_SYMBOL(device_del); EXPORT_SYMBOL(device_unregister); EXPORT_SYMBOL(get_device); EXPORT_SYMBOL(put_device); diff --git a/drivers/base/power.c b/drivers/base/power.c index 1cd4027feffa..594a793e24f9 100644 --- a/drivers/base/power.c +++ b/drivers/base/power.c @@ -38,7 +38,7 @@ int device_suspend(u32 state, u32 level) down(&device_sem); list_for_each(node,&global_device_list) { struct device * dev = to_dev(node); - if (device_present(dev) && dev->driver && dev->driver->suspend) { + if (dev->driver && dev->driver->suspend) { pr_debug("suspending device %s\n",dev->name); error = dev->driver->suspend(dev,state,level); if (error) @@ -64,7 +64,7 @@ void device_resume(u32 level) down(&device_sem); list_for_each_prev(node,&global_device_list) { struct device * dev = to_dev(node); - if (device_present(dev) && dev->driver && dev->driver->resume) { + if (dev->driver && dev->driver->resume) { pr_debug("resuming device %s\n",dev->name); dev->driver->resume(dev,level); } @@ -86,7 +86,7 @@ void device_shutdown(void) down(&device_sem); list_for_each(entry,&global_device_list) { struct device * dev = to_dev(entry); - if (device_present(dev) && dev->driver && dev->driver->shutdown) { + if (dev->driver && dev->driver->shutdown) { pr_debug("shutting down %s\n",dev->name); dev->driver->shutdown(dev); } diff --git a/include/linux/device.h b/include/linux/device.h index b434f2725c23..16ed478db4f1 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -279,12 +279,6 @@ struct device { char name[DEVICE_NAME_SIZE]; /* descriptive ascii string */ char bus_id[BUS_ID_SIZE]; /* position on parent bus */ - spinlock_t lock; /* lock for the device to ensure two - different layers don't access it at - the same time. */ - atomic_t refcount; /* refcount to make sure the device - * persists for the right amount of time */ - struct bus_type * bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ @@ -296,7 +290,6 @@ struct device { void *platform_data; /* Platform specific data (e.g. ACPI, BIOS data relevant to device) */ - enum device_state state; u32 power_state; /* Current operating state. In ACPI-speak, this is D0-D3, D0 being fully functional, and D3 @@ -369,24 +362,6 @@ extern int (*platform_notify)(struct device * dev); extern int (*platform_notify_remove)(struct device * dev); -static inline int device_present(struct device * dev) -{ - return (dev && (dev->state == DEVICE_INITIALIZED || dev->state == DEVICE_REGISTERED)); -} - -/* device and bus locking helpers. - * - * FIXME: Is there anything else we need to do? - */ -static inline void lock_device(struct device * dev) -{ - spin_lock(&dev->lock); -} - -static inline void unlock_device(struct device * dev) -{ - spin_unlock(&dev->lock); -} /** * get_device - atomically increment the reference count for the device. -- cgit v1.2.3 From 9de88958343f0628e08d6117c9dae8d31b858cbb Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 19 Nov 2002 07:07:34 -0800 Subject: [PATCH] rename get_lease to break_lease Al pointed out that the current name of get_lease is extremely confusing and I agree. This (a) renames it to break_lease and (b) fixes a bug noticed by Dave Hansen which could cause a NULL pointer dereference under high load. --- fs/locks.c | 6 +++--- fs/namei.c | 2 +- fs/nfsd/vfs.c | 4 ++-- fs/open.c | 2 +- include/linux/fs.h | 8 ++++---- kernel/ksyms.c | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/locks.c b/fs/locks.c index 8efbc74ec1b8..d2b55e36a7da 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -994,16 +994,16 @@ static void time_out_leases(struct inode *inode) } /** - * __get_lease - revoke all outstanding leases on file + * __break_lease - revoke all outstanding leases on file * @inode: the inode of the file to return * @mode: the open mode (read or write) * - * get_lease (inlined for speed) has checked there already + * break_lease (inlined for speed) has checked there already * is a lease on this file. Leases are broken on a call to open() * or truncate(). This function can sleep unless you * specified %O_NONBLOCK to your open(). */ -int __get_lease(struct inode *inode, unsigned int mode) +int __break_lease(struct inode *inode, unsigned int mode) { int error = 0, future; struct file_lock *new_fl, *flock; diff --git a/fs/namei.c b/fs/namei.c index b2dc8dffd6ca..28a517574b66 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1183,7 +1183,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) /* * Ensure there are no outstanding leases on the file. */ - error = get_lease(inode, flag); + error = break_lease(inode, flag); if (error) return error; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index e706ce2ffd2d..34486025974b 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -259,7 +259,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, * If we are changing the size of the file, then * we need to break all leases. */ - err = get_lease(inode, FMODE_WRITE); + err = break_lease(inode, FMODE_WRITE); if (err) goto out_nfserr; @@ -453,7 +453,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, * Check to see if there are any leases on this file. * This may block while leases are broken. */ - err = get_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0); + err = break_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0); if (err) goto out_nfserr; diff --git a/fs/open.c b/fs/open.c index 6b01ca03d017..19f9cd53a861 100644 --- a/fs/open.c +++ b/fs/open.c @@ -131,7 +131,7 @@ static inline long do_sys_truncate(const char * path, loff_t length) /* * Make sure that there are no leases. */ - error = get_lease(inode, FMODE_WRITE); + error = break_lease(inode, FMODE_WRITE); if (error) goto dput_and_out; diff --git a/include/linux/fs.h b/include/linux/fs.h index 400a5674083b..7aa6498d7d5d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -578,7 +578,7 @@ extern int posix_lock_file(struct file *, struct file_lock *); extern void posix_block_lock(struct file_lock *, struct file_lock *); extern void posix_unblock_lock(struct file *, struct file_lock *); extern int posix_locks_deadlock(struct file_lock *, struct file_lock *); -extern int __get_lease(struct inode *inode, unsigned int flags); +extern int __break_lease(struct inode *inode, unsigned int flags); extern void lease_get_mtime(struct inode *, struct timespec *time); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); @@ -1052,10 +1052,10 @@ static inline int locks_verify_truncate(struct inode *inode, return 0; } -static inline int get_lease(struct inode *inode, unsigned int mode) +static inline int break_lease(struct inode *inode, unsigned int mode) { - if (inode->i_flock && (inode->i_flock->fl_flags & FL_LEASE)) - return __get_lease(inode, mode); + if (inode->i_flock) + return __break_lease(inode, mode); return 0; } diff --git a/kernel/ksyms.c b/kernel/ksyms.c index c54fadff0c0d..c2f7e5bc4dde 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -287,7 +287,7 @@ EXPORT_SYMBOL(page_follow_link); EXPORT_SYMBOL(page_symlink_inode_operations); EXPORT_SYMBOL(page_symlink); EXPORT_SYMBOL(vfs_readdir); -EXPORT_SYMBOL(__get_lease); +EXPORT_SYMBOL(__break_lease); EXPORT_SYMBOL(lease_get_mtime); EXPORT_SYMBOL(lock_may_read); EXPORT_SYMBOL(lock_may_write); -- cgit v1.2.3 From 5c0f111ca088a1a9dbfe6717610cb21711831359 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 19 Nov 2002 17:31:18 -0800 Subject: [PATCH] module device table restoration Patch from Adam Richter. I have a nicer solution based on aliases, but it requires coordination with USB, PCI and PCMCIA maintainers, which is taking time. This restores the old code in the meantime: one week without this is too long for people who need it. --- include/linux/module.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/module.h b/include/linux/module.h index 47ff2bc63e33..927341a26c0e 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -28,8 +28,6 @@ #define MODULE_AUTHOR(name) #define MODULE_DESCRIPTION(desc) #define MODULE_SUPPORTED_DEVICE(name) -#define MODULE_GENERIC_TABLE(gtype,name) -#define MODULE_DEVICE_TABLE(type,name) #define MODULE_PARM_DESC(var,desc) #define print_modules() @@ -41,14 +39,28 @@ struct kernel_symbol }; #ifdef MODULE + +#define MODULE_GENERIC_TABLE(gtype,name) \ +static const unsigned long __module_##gtype##_size \ + __attribute__ ((unused)) = sizeof(struct gtype##_id); \ +static const struct gtype##_id * __module_##gtype##_table \ + __attribute__ ((unused)) = name + /* This is magically filled in by the linker, but THIS_MODULE must be a constant so it works in initializers. */ extern struct module __this_module; #define THIS_MODULE (&__this_module) -#else + +#else /* !MODULE */ + +#define MODULE_GENERIC_TABLE(gtype,name) #define THIS_MODULE ((struct module *)0) + #endif +#define MODULE_DEVICE_TABLE(type,name) \ + MODULE_GENERIC_TABLE(type##_device,name) + struct kernel_symbol_group { /* Links us into the global symbol list */ -- cgit v1.2.3 From 72c85a120f2e9a6deb5a3059108467d54bcbbb6f Mon Sep 17 00:00:00 2001 From: "Andries E. Brouwer" Date: Tue, 19 Nov 2002 17:36:12 -0800 Subject: [PATCH] *_mknod prototype The dev_t argument of sys_mknod is passed to vfs_mknod, and is then cast to int when foo_mknod is called, and is subsequently very often cast back to dev_t. (For example, minix_mknod() calls minix_set_inode() that takes a dev_t.) This is a cleanup that avoids this back-and-forth casting by giving foo_mknod a prototype with dev_t. In most cases now the dev_t is transmitted untouched until init_special_inode. It also makes the two routines hugetlbfs_get_inode() and shmem_get_inode() static. --- Documentation/filesystems/Locking | 2 +- Documentation/filesystems/vfs.txt | 2 +- drivers/hotplug/pci_hotplug_core.c | 4 ++-- drivers/usb/core/inode.c | 4 ++-- fs/coda/dir.c | 5 ++--- fs/coda/upcall.c | 4 ++-- fs/devfs/base.c | 2 +- fs/devices.c | 5 +++-- fs/ext2/namei.c | 2 +- fs/ext3/namei.c | 2 +- fs/hpfs/hpfs_fn.h | 2 +- fs/hpfs/namei.c | 2 +- fs/hugetlbfs/inode.c | 6 ++++-- fs/intermezzo/dir.c | 2 +- fs/jffs/inode-v23.c | 2 +- fs/jffs2/dir.c | 4 ++-- fs/jfs/namei.c | 2 +- fs/minix/namei.c | 2 +- fs/ncpfs/dir.c | 6 +++--- fs/ncpfs/ncplib_kernel.h | 2 +- fs/nfs/dir.c | 5 +++-- fs/ramfs/inode.c | 7 ++++--- fs/reiserfs/namei.c | 2 +- fs/smbfs/dir.c | 4 ++-- fs/sysfs/inode.c | 2 +- fs/sysv/namei.c | 2 +- fs/udf/namei.c | 2 +- fs/ufs/namei.c | 2 +- fs/umsdos/namei.c | 4 ++-- fs/xfs/linux/xfs_iops.c | 2 +- include/linux/coda_psdev.h | 10 +++++----- include/linux/fs.h | 4 ++-- include/linux/umsdos_fs.p | 2 +- include/linux/umsdos_fs_i.h | 6 +++--- mm/shmem.c | 6 ++++-- 35 files changed, 64 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 9ed995b75fbf..2351591b275c 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -35,7 +35,7 @@ prototypes: int (*symlink) (struct inode *,struct dentry *,const char *); int (*mkdir) (struct inode *,struct dentry *,int); int (*rmdir) (struct inode *,struct dentry *); - int (*mknod) (struct inode *,struct dentry *,int,int); + int (*mknod) (struct inode *,struct dentry *,int,dev_t); int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *); int (*readlink) (struct dentry *, char *,int); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index d14db016e09d..09c219cfb284 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -258,7 +258,7 @@ struct inode_operations { int (*symlink) (struct inode *,struct dentry *,const char *); int (*mkdir) (struct inode *,struct dentry *,int); int (*rmdir) (struct inode *,struct dentry *); - int (*mknod) (struct inode *,struct dentry *,int,int); + int (*mknod) (struct inode *,struct dentry *,int,dev_t); int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *); int (*readlink) (struct dentry *, char *,int); diff --git a/drivers/hotplug/pci_hotplug_core.c b/drivers/hotplug/pci_hotplug_core.c index 9ada504c26a7..b6ab87135c30 100644 --- a/drivers/hotplug/pci_hotplug_core.c +++ b/drivers/hotplug/pci_hotplug_core.c @@ -121,7 +121,7 @@ static struct proc_dir_entry *slotdir = NULL; static const char *slotdir_name = "slots"; #endif -static struct inode *pcihpfs_get_inode (struct super_block *sb, int mode, int dev) +static struct inode *pcihpfs_get_inode (struct super_block *sb, int mode, dev_t dev) { struct inode *inode = new_inode(sb); @@ -153,7 +153,7 @@ static struct inode *pcihpfs_get_inode (struct super_block *sb, int mode, int de } /* SMP-safe */ -static int pcihpfs_mknod (struct inode *dir, struct dentry *dentry, int mode, int dev) +static int pcihpfs_mknod (struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode *inode = pcihpfs_get_inode(dir->i_sb, mode, dev); int error = -ENOSPC; diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index a7306cf26a35..ee5900f3969f 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -143,7 +143,7 @@ static int parse_options(struct super_block *s, char *data) /* --------------------------------------------------------------------- */ -static struct inode *usbfs_get_inode (struct super_block *sb, int mode, int dev) +static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t dev) { struct inode *inode = new_inode(sb); @@ -176,7 +176,7 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, int dev) /* SMP-safe */ static int usbfs_mknod (struct inode *dir, struct dentry *dentry, int mode, - int dev) + dev_t dev) { struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev); int error = -EPERM; diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 4f378704b4a8..a7952879bd8f 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -29,7 +29,7 @@ /* dir inode-ops */ static int coda_create(struct inode *dir, struct dentry *new, int mode); -static int coda_mknod(struct inode *dir, struct dentry *new, int mode, int rdev); +static int coda_mknod(struct inode *dir, struct dentry *new, int mode, dev_t rdev); static struct dentry *coda_lookup(struct inode *dir, struct dentry *target); static int coda_link(struct dentry *old_dentry, struct inode *dir_inode, struct dentry *entry); @@ -230,7 +230,7 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode) return 0; } -static int coda_mknod(struct inode *dir, struct dentry *de, int mode, int rdev) +static int coda_mknod(struct inode *dir, struct dentry *de, int mode, dev_t rdev) { int error=0; const char *name=de->d_name.name; @@ -740,4 +740,3 @@ return_bad: unlock_kernel(); return -EIO; } - diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 60de2eaee8af..221bfb88ed4a 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -310,8 +310,8 @@ int venus_rename(struct super_block *sb, struct ViceFid *old_fid, } int venus_create(struct super_block *sb, struct ViceFid *dirfid, - const char *name, int length, int excl, int mode, int rdev, - struct ViceFid *newfid, struct coda_vattr *attrs) + const char *name, int length, int excl, int mode, dev_t rdev, + struct ViceFid *newfid, struct coda_vattr *attrs) { union inputArgs *inp; union outputArgs *outp; diff --git a/fs/devfs/base.c b/fs/devfs/base.c index d54683b572ee..88c5aa160599 100644 --- a/fs/devfs/base.c +++ b/fs/devfs/base.c @@ -3103,7 +3103,7 @@ static int devfs_rmdir (struct inode *dir, struct dentry *dentry) } /* End Function devfs_rmdir */ static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode, - int rdev) + dev_t rdev) { int err; struct fs_info *fs_info = dir->i_sb->s_fs_info; diff --git a/fs/devices.c b/fs/devices.c index 1b8f3e2546e3..adf05053eeb7 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -202,7 +202,7 @@ static struct file_operations bad_sock_fops = { .open = sock_no_open }; -void init_special_inode(struct inode *inode, umode_t mode, int rdev) +void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) { inode->i_mode = mode; if (S_ISCHR(mode)) { @@ -217,5 +217,6 @@ void init_special_inode(struct inode *inode, umode_t mode, int rdev) else if (S_ISSOCK(mode)) inode->i_fop = &bad_sock_fops; else - printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode); + printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", + mode); } diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 8e9e4069ad3b..9f0788e3f6ef 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -134,7 +134,7 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode) return err; } -static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) +static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev) { struct inode * inode = ext2_new_inode (dir, mode); int err = PTR_ERR(inode); diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 98ac54526a5d..e1abbb5552c0 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1616,7 +1616,7 @@ static int ext3_create (struct inode * dir, struct dentry * dentry, int mode) } static int ext3_mknod (struct inode * dir, struct dentry *dentry, - int mode, int rdev) + int mode, dev_t rdev) { handle_t *handle; struct inode *inode; diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index 824d5785c7d8..34a2c5039cc3 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -282,7 +282,7 @@ void hpfs_decide_conv(struct inode *, unsigned char *, unsigned); int hpfs_mkdir(struct inode *, struct dentry *, int); int hpfs_create(struct inode *, struct dentry *, int); -int hpfs_mknod(struct inode *, struct dentry *, int, int); +int hpfs_mknod(struct inode *, struct dentry *, int, dev_t); int hpfs_symlink(struct inode *, struct dentry *, const char *); int hpfs_unlink(struct inode *, struct dentry *); int hpfs_rmdir(struct inode *, struct dentry *); diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index cc2857b95c46..1736980fd020 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -181,7 +181,7 @@ bail: return -ENOSPC; } -int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) +int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { const char *name = dentry->d_name.name; unsigned len = dentry->d_name.len; diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 16da9d0c9e75..2c9b1d26b8fd 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -359,7 +359,8 @@ out: return error; } -struct inode *hugetlbfs_get_inode(struct super_block *sb, int mode, int dev) +static struct inode * +hugetlbfs_get_inode(struct super_block *sb, int mode, dev_t dev) { struct inode * inode = new_inode(sb); @@ -399,7 +400,8 @@ struct inode *hugetlbfs_get_inode(struct super_block *sb, int mode, int dev) * File creation. Allocate an inode, and we're done.. */ /* SMP-safe */ -static int hugetlbfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) +static int +hugetlbfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode * inode = hugetlbfs_get_inode(dir->i_sb, mode, dev); int error = -ENOSPC; diff --git a/fs/intermezzo/dir.c b/fs/intermezzo/dir.c index b1205bf9c7bf..573c318e3138 100644 --- a/fs/intermezzo/dir.c +++ b/fs/intermezzo/dir.c @@ -720,7 +720,7 @@ static int presto_rmdir(struct inode *dir, struct dentry *dentry) return error; } -static int presto_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev) +static int presto_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev) { int error; struct presto_cache *cache; diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 1349e6027ca0..1b0b9fa5070f 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -1072,7 +1072,7 @@ jffs_remove_end: static int -jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) +jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct jffs_raw_inode raw_inode; struct jffs_file *dir_f; diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 68c34e81137b..9c60f565699a 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -32,7 +32,7 @@ static int jffs2_unlink (struct inode *,struct dentry *); static int jffs2_symlink (struct inode *,struct dentry *,const char *); static int jffs2_mkdir (struct inode *,struct dentry *,int); static int jffs2_rmdir (struct inode *,struct dentry *); -static int jffs2_mknod (struct inode *,struct dentry *,int,int); +static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t); static int jffs2_rename (struct inode *, struct dentry *, struct inode *, struct dentry *); @@ -573,7 +573,7 @@ static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry) return ret; } -static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, int rdev) +static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t rdev) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 9426deae5094..603c8f7d1322 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1316,7 +1316,7 @@ int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, * * FUNCTION: Create a special file (device) */ -int jfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) +int jfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct btstack btstack; struct component_name dname; diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 01b2e3a2831a..d2b9ae264ce1 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -75,7 +75,7 @@ static struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry) return NULL; } -static int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev) +static int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, dev_t rdev) { int error; struct inode * inode = minix_new_inode(dir, &error); diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 959d512a82cc..58d2fbd70556 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -42,7 +42,7 @@ static int ncp_rmdir(struct inode *, struct dentry *); static int ncp_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); static int ncp_mknod(struct inode * dir, struct dentry *dentry, - int mode, int rdev); + int mode, dev_t rdev); #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) extern int ncp_symlink(struct inode *, struct dentry *, const char *); #else @@ -883,7 +883,7 @@ out_close: } int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, - int rdev, int attributes) + dev_t rdev, int attributes) { struct ncp_server *server = NCP_SERVER(dir); struct ncp_entry_info finfo; @@ -1169,7 +1169,7 @@ out: } static int ncp_mknod(struct inode * dir, struct dentry *dentry, - int mode, int rdev) + int mode, dev_t rdev) { if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) { DPRINTK(KERN_DEBUG "ncp_mknod: mode = 0%o\n", mode); diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index b25803e23272..29c2ec683c77 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -117,7 +117,7 @@ int ncp_dirhandle_alloc(struct ncp_server *, __u8 vol, __u32 dirent, __u8 *dirha int ncp_dirhandle_free(struct ncp_server *, __u8 dirhandle); int ncp_create_new(struct inode *dir, struct dentry *dentry, - int mode, int rdev, int attributes); + int mode, dev_t rdev, int attributes); static inline int ncp_is_nfs_extras(struct ncp_server* server, unsigned int volnum) { #ifdef CONFIG_NCPFS_NFS_NS diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 8f7f90ba5f10..b7f9bf5eb32a 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -45,7 +45,7 @@ static int nfs_rmdir(struct inode *, struct dentry *); static int nfs_unlink(struct inode *, struct dentry *); static int nfs_symlink(struct inode *, struct dentry *, const char *); static int nfs_link(struct dentry *, struct inode *, struct dentry *); -static int nfs_mknod(struct inode *, struct dentry *, int, int); +static int nfs_mknod(struct inode *, struct dentry *, int, dev_t); static int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); @@ -801,7 +801,8 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) /* * See comments for nfs_proc_create regarding failed operations. */ -static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) +static int +nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct iattr attr; struct nfs_fattr fattr; diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index dea3cdd2bd1f..64b5c100cb91 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -47,7 +47,7 @@ static struct backing_dev_info ramfs_backing_dev_info = { .memory_backed = 1, /* Does not contribute to dirty memory */ }; -struct inode *ramfs_get_inode(struct super_block *sb, int mode, int dev) +static struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev) { struct inode * inode = new_inode(sb); @@ -87,14 +87,15 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, int dev) * File creation. Allocate an inode, and we're done.. */ /* SMP-safe */ -static int ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) +static int +ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode * inode = ramfs_get_inode(dir->i_sb, mode, dev); int error = -ENOSPC; if (inode) { d_instantiate(dentry, inode); - dget(dentry); /* Extra count - pin the dentry in core */ + dget(dentry); /* Extra count - pin the dentry in core */ error = 0; } return error; diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index 7a5e99ce610a..c82566d48d2f 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -605,7 +605,7 @@ out_failed: } -static int reiserfs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) +static int reiserfs_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev) { int retval; struct inode * inode; diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index c4793aa522de..b943f0052798 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -31,7 +31,7 @@ static int smb_rmdir(struct inode *, struct dentry *); static int smb_unlink(struct inode *, struct dentry *); static int smb_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); -static int smb_make_node(struct inode *,struct dentry *,int,int); +static int smb_make_node(struct inode *,struct dentry *,int,dev_t); static int smb_link(struct dentry *, struct inode *, struct dentry *); struct file_operations smb_dir_operations = @@ -641,7 +641,7 @@ out: * matches the connection credentials (and we don't know which those are ...) */ static int -smb_make_node(struct inode *dir, struct dentry *dentry, int mode, int dev) +smb_make_node(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { int error; struct iattr attr; diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 758b72b50602..886038498183 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -87,7 +87,7 @@ static struct inode *sysfs_get_inode(struct super_block *sb, int mode, int dev) return inode; } -static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) +static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode *inode; int error = 0; diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 5a6c2246d251..f2988f107696 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -83,7 +83,7 @@ static struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry) return NULL; } -static int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev) +static int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev) { struct inode * inode = sysv_new_inode(dir, mode); int err = PTR_ERR(inode); diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 138b1c8e3deb..b5be4880deac 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -669,7 +669,7 @@ static int udf_create(struct inode *dir, struct dentry *dentry, int mode) return 0; } -static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev) +static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev) { struct inode * inode; struct udf_fileident_bh fibh; diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index b98fb35af72d..24c6c5d2938d 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -108,7 +108,7 @@ static int ufs_create (struct inode * dir, struct dentry * dentry, int mode) return err; } -static int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) +static int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev) { struct inode * inode = ufs_new_inode(dir, mode); int err = PTR_ERR(inode); diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 1ee0e1a35278..3d89ba970a06 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -237,7 +237,7 @@ static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry, * The same is true for directory creation. */ static int umsdos_create_any (struct inode *dir, struct dentry *dentry, - int mode, int rdev, char flags) + int mode, dev_t rdev, char flags) { struct dentry *fake; struct inode *inode; @@ -861,7 +861,7 @@ out_remove: * in particular and other parts of the kernel I guess. */ int UMSDOS_mknod (struct inode *dir, struct dentry *dentry, - int mode, int rdev) + int mode, dev_t rdev) { return umsdos_create_any (dir, dentry, mode, rdev, 0); } diff --git a/fs/xfs/linux/xfs_iops.c b/fs/xfs/linux/xfs_iops.c index b9bb91a68660..97f58864933d 100644 --- a/fs/xfs/linux/xfs_iops.c +++ b/fs/xfs/linux/xfs_iops.c @@ -69,7 +69,7 @@ linvfs_mknod( struct inode *dir, struct dentry *dentry, int mode, - int rdev) + dev_t rdev) { struct inode *ip; vattr_t va; diff --git a/include/linux/coda_psdev.h b/include/linux/coda_psdev.h index defce1be68ce..0e3f73f7a73e 100644 --- a/include/linux/coda_psdev.h +++ b/include/linux/coda_psdev.h @@ -47,13 +47,13 @@ int venus_close(struct super_block *sb, struct ViceFid *fid, int flags, int venus_open(struct super_block *sb, struct ViceFid *fid, int flags, struct file **f); int venus_mkdir(struct super_block *sb, struct ViceFid *dirfid, - const char *name, int length, - struct ViceFid *newfid, struct coda_vattr *attrs); + const char *name, int length, + struct ViceFid *newfid, struct coda_vattr *attrs); int venus_create(struct super_block *sb, struct ViceFid *dirfid, - const char *name, int length, int excl, int mode, int rdev, - struct ViceFid *newfid, struct coda_vattr *attrs) ; + const char *name, int length, int excl, int mode, dev_t rdev, + struct ViceFid *newfid, struct coda_vattr *attrs) ; int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid, - const char *name, int length); + const char *name, int length); int venus_remove(struct super_block *sb, struct ViceFid *dirfid, const char *name, int length); int venus_readlink(struct super_block *sb, struct ViceFid *fid, diff --git a/include/linux/fs.h b/include/linux/fs.h index 7aa6498d7d5d..bbb4e2cacecc 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -773,7 +773,7 @@ struct inode_operations { int (*symlink) (struct inode *,struct dentry *,const char *); int (*mkdir) (struct inode *,struct dentry *,int); int (*rmdir) (struct inode *,struct dentry *); - int (*mknod) (struct inode *,struct dentry *,int,int); + int (*mknod) (struct inode *,struct dentry *,int,dev_t); int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *); int (*readlink) (struct dentry *, char *,int); @@ -1109,7 +1109,7 @@ extern inline const char *bdevname(struct block_device *bdev) } extern const char * cdevname(kdev_t); extern const char * kdevname(kdev_t); -extern void init_special_inode(struct inode *, umode_t, int); +extern void init_special_inode(struct inode *, umode_t, dev_t); /* Invalid inode operations -- fs/bad_inode.c */ extern void make_bad_inode(struct inode *); diff --git a/include/linux/umsdos_fs.p b/include/linux/umsdos_fs.p index 99e6eefa8229..7034b7eb6b16 100644 --- a/include/linux/umsdos_fs.p +++ b/include/linux/umsdos_fs.p @@ -82,7 +82,7 @@ int UMSDOS_mkdir (struct inode *dir, int UMSDOS_mknod (struct inode *dir, struct dentry *dentry, int mode, - int rdev); + dev_t rdev); int UMSDOS_rmdir (struct inode *dir,struct dentry *dentry); int UMSDOS_unlink (struct inode *dir, struct dentry *dentry); int UMSDOS_rename (struct inode *old_dir, diff --git a/include/linux/umsdos_fs_i.h b/include/linux/umsdos_fs_i.h index 0edfb1d541a4..f4c992b44cd2 100644 --- a/include/linux/umsdos_fs_i.h +++ b/include/linux/umsdos_fs_i.h @@ -50,9 +50,9 @@ struct dir_locking_info { struct umsdos_inode_info { struct msdos_inode_info msdos_info; struct dir_locking_info dir_info; - int i_patched; /* Inode has been patched */ - int i_is_hlink; /* Resolved hardlink inode? */ - off_t pos; /* Entry offset in the emd_owner file */ + int i_patched; /* Inode has been patched */ + int i_is_hlink; /* Resolved hardlink inode? */ + off_t pos; /* Entry offset in the emd_owner file */ }; #endif diff --git a/mm/shmem.c b/mm/shmem.c index d8dc724620d4..2325743e2561 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1015,7 +1015,8 @@ static int shmem_mmap(struct file *file, struct vm_area_struct *vma) return 0; } -struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev) +static struct inode * +shmem_get_inode(struct super_block *sb, int mode, dev_t dev) { struct inode *inode; struct shmem_inode_info *info; @@ -1426,7 +1427,8 @@ static int shmem_statfs(struct super_block *sb, struct statfs *buf) /* * File creation. Allocate an inode, and we're done.. */ -static int shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) +static int +shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode *inode = shmem_get_inode(dir->i_sb, mode, dev); int error = -ENOSPC; -- cgit v1.2.3 From 772c0cf1f71bcc2693d2400dc1d962f65625b25d Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Wed, 20 Nov 2002 04:07:30 -0800 Subject: ACPI: Add ec_read and ec_write external functions Other ec.c cleanups, too --- drivers/acpi/acpi_ksyms.c | 9 +++ drivers/acpi/ec.c | 167 ++++++++++++++++++++++++++++------------------ include/linux/acpi.h | 6 ++ 3 files changed, 116 insertions(+), 66 deletions(-) (limited to 'include/linux') diff --git a/drivers/acpi/acpi_ksyms.c b/drivers/acpi/acpi_ksyms.c index ab2f9fa45056..db62f72c586b 100644 --- a/drivers/acpi/acpi_ksyms.c +++ b/drivers/acpi/acpi_ksyms.c @@ -24,6 +24,7 @@ */ #include +#include #include "include/acpi.h" #include "acpi_bus.h" @@ -141,3 +142,11 @@ EXPORT_SYMBOL(acpi_pci_irq_enable); extern int acpi_pci_irq_lookup (int segment, int bus, int device, int pin); EXPORT_SYMBOL(acpi_pci_irq_lookup); #endif /*CONFIG_ACPI_PCI */ + +#ifdef CONFIG_ACPI_EC +/* ACPI EC driver (ec.c) */ + +EXPORT_SYMBOL(ec_read); +EXPORT_SYMBOL(ec_write); +#endif + diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index eecb3b2de6c2..6d4ad3a954c1 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -92,6 +92,9 @@ struct acpi_ec { /* If we find an EC via the ECDT, we need to keep a ptr to its context */ static struct acpi_ec *ec_ecdt; +/* External interfaces use first EC only, so remember */ +static struct acpi_device *first_ec; + /* -------------------------------------------------------------------------- Transaction Management -------------------------------------------------------------------------- */ @@ -236,6 +239,47 @@ end: return_VALUE(result); } +/* + * Externally callable EC access functions. For now, assume 1 EC only + */ +int +ec_read(u8 addr, u8 *val) +{ + struct acpi_ec *ec; + int err; + u32 temp_data; + + if (!first_ec) + return -ENODEV; + + ec = acpi_driver_data(first_ec); + + err = acpi_ec_read(ec, addr, &temp_data); + + if (!err) { + *val = temp_data; + return 0; + } + else + return err; +} + +int +ec_write(u8 addr, u8 val) +{ + struct acpi_ec *ec; + int err; + + if (!first_ec) + return -ENODEV; + + ec = acpi_driver_data(first_ec); + + err = acpi_ec_write(ec, addr, val); + + return err; +} + static int acpi_ec_query ( @@ -540,11 +584,15 @@ acpi_ec_add ( acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); /* If our UID matches the UID for the ECDT-enumerated EC, - we already found this EC, so abort. */ + we now have the *real* EC info, so kill the makeshift one.*/ acpi_evaluate_integer(ec->handle, "_UID", NULL, &uid); if (ec_ecdt && ec_ecdt->uid == uid) { - result = -ENODEV; - goto end; + acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); + + acpi_remove_gpe_handler(ec_ecdt->gpe_bit, &acpi_ec_gpe_handler); + + kfree(ec_ecdt); } /* Get GPE bit assignment (EC events). */ @@ -564,6 +612,9 @@ acpi_ec_add ( acpi_device_name(device), acpi_device_bid(device), (u32) ec->gpe_bit); + if (!first_ec) + first_ec = device; + end: if (result) kfree(ec); @@ -584,7 +635,7 @@ acpi_ec_remove ( if (!device) return_VALUE(-EINVAL); - ec = (struct acpi_ec *) acpi_driver_data(device); + ec = acpi_driver_data(device); acpi_ec_remove_fs(device); @@ -609,7 +660,7 @@ acpi_ec_start ( if (!device) return_VALUE(-EINVAL); - ec = (struct acpi_ec *) acpi_driver_data(device); + ec = acpi_driver_data(device); if (!ec) return_VALUE(-EINVAL); @@ -688,7 +739,7 @@ acpi_ec_stop ( if (!device) return_VALUE(-EINVAL); - ec = (struct acpi_ec *) acpi_driver_data(device); + ec = acpi_driver_data(device); status = acpi_remove_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); @@ -711,50 +762,50 @@ acpi_ec_ecdt_probe (void) status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING, (acpi_table_header **) &ecdt_ptr); - if (ACPI_SUCCESS(status)) { - printk(KERN_INFO PREFIX "Found ECDT\n"); - - /* - * TODO: When the new driver model allows it, simply tell the - * EC driver it has a new device via that, instead if this. - */ - ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); - if (!ec_ecdt) - return -ENOMEM; - memset(ec_ecdt, 0, sizeof(struct acpi_ec)); - - ec_ecdt->command_addr = ecdt_ptr->ec_control; - ec_ecdt->status_addr = ecdt_ptr->ec_control; - ec_ecdt->data_addr = ecdt_ptr->ec_data; - ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; - ec_ecdt->lock = SPIN_LOCK_UNLOCKED; - /* use the GL just to be safe */ - ec_ecdt->global_lock = TRUE; - ec_ecdt->uid = ecdt_ptr->uid; - - status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle); - if (ACPI_FAILURE(status)) { - goto error; - } - - /* - * Install GPE handler - */ - status = acpi_install_gpe_handler(ec_ecdt->gpe_bit, - ACPI_EVENT_EDGE_TRIGGERED, &acpi_ec_gpe_handler, - ec_ecdt); - if (ACPI_FAILURE(status)) { - goto error; - } - - status = acpi_install_address_space_handler (ACPI_ROOT_OBJECT, - ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, - &acpi_ec_space_setup, ec_ecdt); - if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(ec_ecdt->gpe_bit, - &acpi_ec_gpe_handler); - goto error; - } + if (ACPI_FAILURE(status)) + return 0; + + printk(KERN_INFO PREFIX "Found ECDT\n"); + + /* + * Generate a temporary ec context to use until the namespace is scanned + */ + ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + if (!ec_ecdt) + return -ENOMEM; + memset(ec_ecdt, 0, sizeof(struct acpi_ec)); + + ec_ecdt->command_addr = ecdt_ptr->ec_control; + ec_ecdt->status_addr = ecdt_ptr->ec_control; + ec_ecdt->data_addr = ecdt_ptr->ec_data; + ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; + ec_ecdt->lock = SPIN_LOCK_UNLOCKED; + /* use the GL just to be safe */ + ec_ecdt->global_lock = TRUE; + ec_ecdt->uid = ecdt_ptr->uid; + + status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle); + if (ACPI_FAILURE(status)) { + goto error; + } + + /* + * Install GPE handler + */ + status = acpi_install_gpe_handler(ec_ecdt->gpe_bit, + ACPI_EVENT_EDGE_TRIGGERED, &acpi_ec_gpe_handler, + ec_ecdt); + if (ACPI_FAILURE(status)) { + goto error; + } + + status = acpi_install_address_space_handler (ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, + &acpi_ec_space_setup, ec_ecdt); + if (ACPI_FAILURE(status)) { + acpi_remove_gpe_handler(ec_ecdt->gpe_bit, + &acpi_ec_gpe_handler); + goto error; } return 0; @@ -795,20 +846,6 @@ subsys_initcall(acpi_ec_init); /* EC driver currently not unloadable */ #if 0 -static void __exit -acpi_ec_ecdt_exit (void) -{ - if (!ec_ecdt) - return; - - acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, - ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); - - acpi_remove_gpe_handler(ec_ecdt->gpe_bit, &acpi_ec_gpe_handler); - - kfree(ec_ecdt); -} - static void __exit acpi_ec_exit (void) { @@ -818,8 +855,6 @@ acpi_ec_exit (void) remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir); - acpi_ec_ecdt_exit(); - return_VOID; } #endif /* 0 */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 698a9a3b5dbc..5d5eaeb07738 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -419,6 +419,12 @@ int acpi_pci_irq_init (void); #endif /*CONFIG_ACPI_PCI*/ +#ifdef CONFIG_ACPI_EC + +int ec_read(u8 addr, u8 *val); +int ec_write(u8 addr, u8 val); + +#endif /*CONFIG_ACPI_EC*/ #ifdef CONFIG_ACPI -- cgit v1.2.3 From 3c8d66aa65509f40ac6666693c62600d94e7ea8b Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Wed, 20 Nov 2002 08:08:19 -0600 Subject: driver model: update and clean bus and driver support. This a multi-pronged attack aimed at exploiting the kobject infrastructure mor. - Remove bus_driver_list, in favor of list in bus_subys. - Remove bus_for_each_* and driver_for_each_dev(). They're not being used by anyone, have questionable locking semantics, and really don't provide that much use, as the function returns once the callback fails, with no indication of where it failed. Forget them, at least for now. - Make sure that we return success from bus_match() if device matches, but doesn't have a probe method. - Remove extraneous get_{device,driver}s from bus routines that are serialized by the bus's rwsem. bus_{add,remove}_{device,driver} all take the rwsem, so there is no way we can get a non-existant object when in those functions. - Use the rwsem in the struct subsystem the bus has embedded in it, and kill the separate one in struct bus_type. - Move bulk of driver_register() into bus_add_driver(), which holds the bus's rwsem during the entirety. this will prevent the driver from being unloaded while it's being registered, and two drivers with the same name getting registered at the same time. - Ditto for driver_unregister() and bus_remove_driver(). - Add driver_release() method for the driver bus driver subsystems. (Explained later) - Use only the refcounts in the buses' kobjects, and kill the one in struct bus_type. - Kill struct bus_type::present and struct device_driver::present. These didn't work out the way we intended them to. The idea was to not let a user obtain a rerference count to the object if it was in the process of being unregistered. All the code paths should be fixed now such that their registration is protected with a semaphore, so no partially initialized objects can be removed, and enough infrastructure is moved to the kobject model so that once the object is publically visible, it should be usable by other sources. - Add a bus_sem to serialize bus registration and unregistration. - Add struct device_driver::unload_sem to prevent unloading of drivers with a positive reference count. The driver model has always had a bug that would allow a driver with a positive reference count to be unloaded. It would decrement the reference count and return, letting the module be unloaded, without accounting for the other users of the object. This has been discussed many times, though never resolved cleanly. This should fix the problem in the simplest manner. struct device_driver gets unload_sem, which is initialized to _locked_. When the reference count for the driver reaches 0, the semaphore is unlocked. driver_unregister() blocks on acquiring this lock before it exits. In the normal case that driver_unregister() drops the last reference to the driver, the lock will be acquired immediately, and the module will unload. In the case that someone else is using the driver object, driver_unregister() will not be able to acquire the lock, since the refcount has not reached 0, and the lock has not been released. This means that rmmod(8) will block while drivers' sysfs files are open. There are no sysfs files for drivers yet, but note this when they do have some. --- drivers/base/bus.c | 284 ++++++++++++++++++++++++++----------------------- drivers/base/driver.c | 129 +++++++++------------- include/linux/device.h | 20 +--- 3 files changed, 203 insertions(+), 230 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 17145af25b1b..d18effa7f981 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -16,7 +16,7 @@ #include #include "base.h" -static LIST_HEAD(bus_driver_list); +static DECLARE_MUTEX(bus_sem); #define to_dev(node) container_of(node,struct device,bus_list) #define to_drv(node) container_of(node,struct device_driver,bus_list) @@ -64,8 +64,15 @@ static struct sysfs_ops driver_sysfs_ops = { }; +static void driver_release(struct kobject * kobj) +{ + struct device_driver * drv = to_driver(kobj); + up(&drv->unload_sem); +} + + /* - * sysfs bindings for drivers + * sysfs bindings for buses */ @@ -124,74 +131,14 @@ struct subsystem bus_subsys = { .sysfs_ops = &bus_sysfs_ops, }; + /** - * bus_for_each_dev - walk list of devices and do something to each - * @bus: bus in question - * @data: data for the callback - * @callback: caller-defined action to perform on each device - * - * Why do we do this? So we can guarantee proper locking and reference - * counting on devices as we touch each one. - * - * Algorithm: - * Take device_lock and get the first node in the list. - * Try and increment the reference count on it. If we can't, it's in the - * process of being removed, but that process hasn't acquired device_lock. - * It's still in the list, so we grab the next node and try that one. - * We drop the lock to call the callback. - * We can't decrement the reference count yet, because we need the next - * node in the list. So, we set @prev to point to the current device. - * On the next go-round, we decrement the reference count on @prev, so if - * it's being removed, it won't affect us. + * attach - add device to driver. + * @dev: device. + * + * By this point, we know for sure what the driver is, so we + * do the rest (which ain't that much). */ -int bus_for_each_dev(struct bus_type * bus, void * data, - int (*callback)(struct device * dev, void * data)) -{ - struct list_head * node; - int error = 0; - - bus = get_bus(bus); - if (bus) { - down_read(&bus->rwsem); - list_for_each(node,&bus->devices) { - struct device * dev = get_device(to_dev(node)); - if (dev) { - error = callback(dev,data); - put_device(dev); - if (error) - break; - } - } - up_read(&bus->rwsem); - put_bus(bus); - } - return error; -} - -int bus_for_each_drv(struct bus_type * bus, void * data, - int (*callback)(struct device_driver * drv, void * data)) -{ - struct list_head * node; - int error = 0; - - bus = get_bus(bus); - if (bus) { - down_read(&bus->rwsem); - list_for_each(node,&bus->drivers) { - struct device_driver * drv = get_driver(to_drv(node)); - if (drv) { - error = callback(drv,data); - put_driver(drv); - if (error) - break; - } - } - up_read(&bus->rwsem); - put_bus(bus); - } - return error; -} - static void attach(struct device * dev) { pr_debug("bound device '%s' to driver '%s'\n", @@ -200,6 +147,21 @@ static void attach(struct device * dev) sysfs_create_link(&dev->driver->kobj,&dev->kobj,dev->kobj.name); } + +/** + * bus_match - check compatibility between device & driver. + * @dev: device. + * @drv: driver. + * + * First, we call the bus's match function, which should compare + * the device IDs the driver supports with the device IDs of the + * device. Note we don't do this ourselves because we don't know + * the format of the ID structures, nor what is to be considered + * a match and what is not. + * + * If we find a match, we call @drv->probe(@dev) if it exists, and + * call attach() above. + */ static int bus_match(struct device * dev, struct device_driver * drv) { int error = -ENODEV; @@ -208,14 +170,24 @@ static int bus_match(struct device * dev, struct device_driver * drv) if (drv->probe) { if (!(error = drv->probe(dev))) attach(dev); - else + else dev->driver = NULL; - } else + } else { attach(dev); + error = 0; + } } return error; } + +/** + * device_attach - try to attach device to a driver. + * @dev: device. + * + * Walk the list of drivers that the bus has and call bus_match() + * for each pair. If a compatible pair is found, break out and return. + */ static int device_attach(struct device * dev) { struct bus_type * bus = dev->bus; @@ -232,17 +204,24 @@ static int device_attach(struct device * dev) list_for_each(entry,&bus->drivers) { struct device_driver * drv = - get_driver(container_of(entry,struct device_driver,bus_list)); - if (!drv) - continue; - error = bus_match(dev,drv); - put_driver(drv); - if (!error) + container_of(entry,struct device_driver,bus_list); + if (!(error = bus_match(dev,drv))) break; } return error; } + +/** + * driver_attach - try to bind driver to devices. + * @drv: driver. + * + * Walk the list of devices that the bus has on it and try to match + * the driver with each one. + * If bus_match() returns 0 and the @dev->driver is set, we've found + * a compatible pair, so we call devclass_add_device() to add the + * device to the class. + */ static int driver_attach(struct device_driver * drv) { struct bus_type * bus = drv->bus; @@ -254,17 +233,24 @@ static int driver_attach(struct device_driver * drv) list_for_each(entry,&bus->devices) { struct device * dev = container_of(entry,struct device,bus_list); - if (get_device(dev)) { - if (!dev->driver) { - if (!bus_match(dev,drv) && dev->driver) - devclass_add_device(dev); - } - put_device(dev); + if (!dev->driver) { + if (!bus_match(dev,drv) && dev->driver) + devclass_add_device(dev); } } return error; } + +/** + * detach - do dirty work of detaching device from its driver. + * @dev: device. + * @drv: its driver. + * + * Note that calls to this function are serialized by taking the + * bus's rwsem in both bus_remove_device() and bus_remove_driver(). + */ + static void detach(struct device * dev, struct device_driver * drv) { if (drv) { @@ -277,120 +263,152 @@ static void detach(struct device * dev, struct device_driver * drv) } } + +/** + * device_detach - detach device from its driver. + * @dev: device. + */ + static void device_detach(struct device * dev) { detach(dev,dev->driver); } + +/** + * driver_detach - detach driver from all devices it controls. + * @drv: driver. + */ + static void driver_detach(struct device_driver * drv) { struct list_head * entry, * next; list_for_each_safe(entry,next,&drv->devices) { struct device * dev = container_of(entry,struct device,driver_list); - if (get_device(dev)) { - detach(dev,drv); - put_device(dev); - } + detach(dev,drv); } } /** - * bus_add_device - add device to bus - * @dev: device being added + * bus_add_device - add device to bus + * @dev: device being added * - * Add the device to its bus's list of devices. - * Create a symlink in the bus's 'devices' directory to the - * device's physical location. - * Try and bind the device to a driver. + * - Add the device to its bus's list of devices. + * - Try to attach to driver. + * - Create link to device's physical location. */ int bus_add_device(struct device * dev) { struct bus_type * bus = get_bus(dev->bus); if (bus) { - down_write(&dev->bus->rwsem); + down_write(&dev->bus->subsys.rwsem); pr_debug("bus %s: add device %s\n",bus->name,dev->bus_id); list_add_tail(&dev->bus_list,&dev->bus->devices); device_attach(dev); - up_write(&dev->bus->rwsem); + up_write(&dev->bus->subsys.rwsem); sysfs_create_link(&bus->devsubsys.kobj,&dev->kobj,dev->bus_id); } return 0; } /** - * bus_remove_device - remove device from bus - * @dev: device to be removed + * bus_remove_device - remove device from bus + * @dev: device to be removed * - * Remove symlink from bus's directory. - * Delete device from bus's list. + * - Remove symlink from bus's directory. + * - Delete device from bus's list. + * - Detach from its driver. + * - Drop reference taken in bus_add_device(). */ void bus_remove_device(struct device * dev) { if (dev->bus) { sysfs_remove_link(&dev->bus->devsubsys.kobj,dev->bus_id); - down_write(&dev->bus->rwsem); + down_write(&dev->bus->subsys.rwsem); pr_debug("bus %s: remove device %s\n",dev->bus->name,dev->bus_id); device_detach(dev); list_del_init(&dev->bus_list); - up_write(&dev->bus->rwsem); + up_write(&dev->bus->subsys.rwsem); put_bus(dev->bus); } } + +/** + * bus_add_driver - Add a driver to the bus. + * @drv: driver. + * + */ int bus_add_driver(struct device_driver * drv) { struct bus_type * bus = get_bus(drv->bus); if (bus) { - down_write(&bus->rwsem); + down_write(&bus->subsys.rwsem); pr_debug("bus %s: add driver %s\n",bus->name,drv->name); + + strncpy(drv->kobj.name,drv->name,KOBJ_NAME_LEN); + drv->kobj.subsys = &bus->drvsubsys; + kobject_register(&drv->kobj); + + devclass_add_driver(drv); list_add_tail(&drv->bus_list,&bus->drivers); driver_attach(drv); - up_write(&bus->rwsem); + up_write(&bus->subsys.rwsem); } return 0; } + +/** + * bus_remove_driver - delete driver from bus's knowledge. + * @drv: driver. + * + * Detach the driver from the devices it controls, and remove + * it from it's bus's list of drivers. Finally, we drop the reference + * to the bus we took in bus_add_driver(). + */ + void bus_remove_driver(struct device_driver * drv) { if (drv->bus) { - down_write(&drv->bus->rwsem); + down_write(&drv->bus->subsys.rwsem); pr_debug("bus %s: remove driver %s\n",drv->bus->name,drv->name); driver_detach(drv); list_del_init(&drv->bus_list); - up_write(&drv->bus->rwsem); + devclass_remove_driver(drv); + kobject_unregister(&drv->kobj); + up_write(&drv->bus->subsys.rwsem); + put_bus(drv->bus); } } struct bus_type * get_bus(struct bus_type * bus) { - struct bus_type * ret = bus; - spin_lock(&device_lock); - if (bus && bus->present && atomic_read(&bus->refcount)) - atomic_inc(&bus->refcount); - else - ret = NULL; - spin_unlock(&device_lock); - return ret; + return bus ? container_of(subsys_get(&bus->subsys),struct bus_type,subsys) : NULL; } void put_bus(struct bus_type * bus) { - if (!atomic_dec_and_lock(&bus->refcount,&device_lock)) - return; - list_del_init(&bus->node); - spin_unlock(&device_lock); - WARN_ON(bus->present); + subsys_put(&bus->subsys); } +/** + * bus_register - register a bus with the system. + * @bus: bus. + * + * We take bus_sem here to protect against the bus being + * unregistered during the registration process. + * Once we have that, we registered the bus with the kobject + * infrastructure, then register the children subsystems it has: + * the devices and drivers that belong to the bus. + */ int bus_register(struct bus_type * bus) { - init_rwsem(&bus->rwsem); INIT_LIST_HEAD(&bus->devices); INIT_LIST_HEAD(&bus->drivers); - atomic_set(&bus->refcount,2); - bus->present = 1; + down(&bus_sem); strncpy(bus->subsys.kobj.name,bus->name,KOBJ_NAME_LEN); bus->subsys.parent = &bus_subsys; subsystem_register(&bus->subsys); @@ -402,28 +420,32 @@ int bus_register(struct bus_type * bus) snprintf(bus->drvsubsys.kobj.name,KOBJ_NAME_LEN,"drivers"); bus->drvsubsys.parent = &bus->subsys; bus->drvsubsys.sysfs_ops = &driver_sysfs_ops; + bus->drvsubsys.release = driver_release; subsystem_register(&bus->drvsubsys); - spin_lock(&device_lock); - list_add_tail(&bus->node,&bus_driver_list); - spin_unlock(&device_lock); - pr_debug("bus type '%s' registered\n",bus->name); - put_bus(bus); + up(&bus_sem); return 0; } + +/** + * bus_unregister - remove a bus from the system + * @bus: bus. + * + * Take bus_sem, in case the bus we're registering hasn't + * finished registering. Once we have it, unregister the child + * subsystems and the bus itself. + * Finally, we call put_bus() to release the refcount + */ void bus_unregister(struct bus_type * bus) { - spin_lock(&device_lock); - bus->present = 0; - spin_unlock(&device_lock); - + down(&bus_sem); pr_debug("bus %s: unregistering\n",bus->name); subsystem_unregister(&bus->drvsubsys); subsystem_unregister(&bus->devsubsys); subsystem_unregister(&bus->subsys); - put_bus(bus); + up(&bus_sem); } static int __init bus_subsys_init(void) @@ -433,8 +455,6 @@ static int __init bus_subsys_init(void) core_initcall(bus_subsys_init); -EXPORT_SYMBOL(bus_for_each_dev); -EXPORT_SYMBOL(bus_for_each_drv); EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_register); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 0887022e13cd..39d9e70efc48 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -3,7 +3,7 @@ * */ -#undef DEBUG +#define DEBUG #include #include @@ -12,9 +12,12 @@ #include "base.h" #define to_dev(node) container_of(node,struct device,driver_list) +#define to_drv(obj) container_of(obj,struct device_driver,kobj) -/* - * helpers for creating driver attributes in sysfs +/** + * driver_create_file - create sysfs file for driver. + * @drv: driver. + * @attr: driver attribute descriptor. */ int driver_create_file(struct device_driver * drv, struct driver_attribute * attr) @@ -28,6 +31,13 @@ int driver_create_file(struct device_driver * drv, struct driver_attribute * att return error; } + +/** + * driver_remove_file - remove sysfs file for driver. + * @drv: driver. + * @attr: driver attribute descriptor. + */ + void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr) { if (get_driver(drv)) { @@ -36,106 +46,67 @@ void driver_remove_file(struct device_driver * drv, struct driver_attribute * at } } -int driver_for_each_dev(struct device_driver * drv, void * data, - int (*callback)(struct device *, void * )) -{ - struct list_head * node; - int error = 0; - - drv = get_driver(drv); - if (drv) { - down_read(&drv->bus->rwsem); - list_for_each(node,&drv->devices) { - struct device * dev = get_device(to_dev(node)); - if (dev) { - error = callback(dev,data); - put_device(dev); - if (error) - break; - } - } - up_read(&drv->bus->rwsem); - put_driver(drv); - } - return error; -} +/** + * get_driver - increment driver reference count. + * @drv: driver. + */ struct device_driver * get_driver(struct device_driver * drv) { - struct device_driver * ret = drv; - spin_lock(&device_lock); - if (drv && drv->present && atomic_read(&drv->refcount) > 0) - atomic_inc(&drv->refcount); - else - ret = NULL; - spin_unlock(&device_lock); - return ret; + return drv ? to_drv(kobject_get(&drv->kobj)) : NULL; } -void remove_driver(struct device_driver * drv) -{ - BUG(); -} - /** - * put_driver - decrement driver's refcount and clean up if necessary - * @drv: driver in question + * put_driver - decrement driver's refcount. + * @drv: driver. */ void put_driver(struct device_driver * drv) { - struct bus_type * bus = drv->bus; - if (!atomic_dec_and_lock(&drv->refcount,&device_lock)) - return; - spin_unlock(&device_lock); - BUG_ON(drv->present); - if (drv->release) - drv->release(drv); - put_bus(bus); + kobject_put(&drv->kobj); } + /** - * driver_register - register driver with bus - * @drv: driver to register - * - * Add to bus's list of devices + * driver_register - register driver with bus + * @drv: driver to register + * + * We pass off most of the work to the bus_add_driver() call, + * since most of the things we have to do deal with the bus + * structures. + * + * The one interesting aspect is that we initialize @drv->unload_sem + * to a locked state here. It will be unlocked when the driver + * reference count reaches 0. */ int driver_register(struct device_driver * drv) { - if (!drv->bus) - return -EINVAL; - - pr_debug("driver %s:%s: registering\n",drv->bus->name,drv->name); - - kobject_init(&drv->kobj); - strncpy(drv->kobj.name,drv->name,KOBJ_NAME_LEN); - drv->kobj.subsys = &drv->bus->drvsubsys; - kobject_register(&drv->kobj); - - get_bus(drv->bus); - atomic_set(&drv->refcount,2); - rwlock_init(&drv->lock); INIT_LIST_HEAD(&drv->devices); - drv->present = 1; - bus_add_driver(drv); - devclass_add_driver(drv); - put_driver(drv); - return 0; + init_MUTEX_LOCKED(&drv->unload_sem); + return bus_add_driver(drv); } + +/** + * driver_unregister - remove driver from system. + * @drv: driver. + * + * Again, we pass off most of the work to the bus-level call. + * + * Though, once that is done, we attempt to take @drv->unload_sem. + * This will block until the driver refcount reaches 0, and it is + * released. Only modular drivers will call this function, and we + * have to guarantee that it won't complete, letting the driver + * unload until all references are gone. + */ + void driver_unregister(struct device_driver * drv) { - spin_lock(&device_lock); - drv->present = 0; - spin_unlock(&device_lock); - pr_debug("driver %s:%s: unregistering\n",drv->bus->name,drv->name); bus_remove_driver(drv); - devclass_remove_driver(drv); - kobject_unregister(&drv->kobj); - put_driver(drv); + down(&drv->unload_sem); + up(&drv->unload_sem); } -EXPORT_SYMBOL(driver_for_each_dev); EXPORT_SYMBOL(driver_register); EXPORT_SYMBOL(driver_unregister); EXPORT_SYMBOL(get_driver); diff --git a/include/linux/device.h b/include/linux/device.h index 16ed478db4f1..f995ac227140 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -64,14 +64,10 @@ struct device_class; struct bus_type { char * name; - struct rw_semaphore rwsem; - atomic_t refcount; - u32 present; struct subsystem subsys; struct subsystem drvsubsys; struct subsystem devsubsys; - struct list_head node; struct list_head devices; struct list_head drivers; @@ -88,11 +84,6 @@ extern void bus_unregister(struct bus_type * bus); extern struct bus_type * get_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus); -extern int bus_for_each_dev(struct bus_type * bus, void * data, - int (*callback)(struct device * dev, void * data)); -extern int bus_for_each_drv(struct bus_type * bus, void * data, - int (*callback)(struct device_driver * drv, void * data)); - /* driverfs interface for exporting bus attributes */ @@ -117,10 +108,7 @@ struct device_driver { struct bus_type * bus; struct device_class * devclass; - rwlock_t lock; - atomic_t refcount; - u32 present; - + struct semaphore unload_sem; struct kobject kobj; struct list_head bus_list; struct list_head class_list; @@ -131,8 +119,6 @@ struct device_driver { void (*shutdown) (struct device * dev); int (*suspend) (struct device * dev, u32 state, u32 level); int (*resume) (struct device * dev, u32 level); - - void (*release) (struct device_driver * drv); }; @@ -141,10 +127,6 @@ extern void driver_unregister(struct device_driver * drv); extern struct device_driver * get_driver(struct device_driver * drv); extern void put_driver(struct device_driver * drv); -extern void remove_driver(struct device_driver * drv); - -extern int driver_for_each_dev(struct device_driver * drv, void * data, - int (*callback)(struct device * dev, void * data)); /* driverfs interface for exporting driver attributes */ -- cgit v1.2.3 From b47b46a39bd5a4ee88eafbfc0345ad1a41490ad0 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Wed, 20 Nov 2002 09:05:27 -0600 Subject: driver model: make classes and interfaces use kobject infrastructure. Like the other objects, this allows a decent bit of cleanup. Details include: - use rwsem in subsytem, instead of one in struct device_class. - use refcount in struct kobject, instead of one in struct device_class. - kill class's present flag. - kill class_list, since we can just use class_subsys's. - make interfaces instances of their class's subsystem. This allows us to kill struct device_class::intf_list, and struct device_interface::node. --- drivers/base/class.c | 48 +++++++++++++++--------------------------------- drivers/base/intf.c | 32 ++++++++++---------------------- include/linux/device.h | 8 -------- 3 files changed, 25 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index 4e7397905841..fbc6326312d8 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -2,6 +2,8 @@ * class.c - basic device class management */ +#undef DEBUG + #include #include #include @@ -101,12 +103,12 @@ int devclass_add_driver(struct device_driver * drv) { struct device_class * cls = get_devclass(drv->devclass); if (cls) { - down_write(&cls->rwsem); + down_write(&cls->subsys.rwsem); pr_debug("device class %s: adding driver %s:%s\n", cls->name,drv->bus->name,drv->name); list_add_tail(&drv->class_list,&cls->drivers); devclass_drv_link(drv); - up_write(&cls->rwsem); + up_write(&cls->subsys.rwsem); } return 0; } @@ -115,12 +117,12 @@ void devclass_remove_driver(struct device_driver * drv) { struct device_class * cls = drv->devclass; if (cls) { - down_write(&cls->rwsem); + down_write(&cls->subsys.rwsem); pr_debug("device class %s: removing driver %s:%s\n", cls->name,drv->bus->name,drv->name); list_del_init(&drv->class_list); devclass_drv_unlink(drv); - up_write(&cls->rwsem); + up_write(&cls->subsys.rwsem); put_devclass(cls); } } @@ -163,7 +165,7 @@ int devclass_add_device(struct device * dev) if (dev->driver) { cls = get_devclass(dev->driver->devclass); if (cls) { - down_write(&cls->rwsem); + down_write(&cls->subsys.rwsem); pr_debug("device class %s: adding device %s\n", cls->name,dev->name); if (cls->add_device) @@ -176,7 +178,7 @@ int devclass_add_device(struct device * dev) /* notify userspace (call /sbin/hotplug) */ class_hotplug (dev, "add"); - up_write(&cls->rwsem); + up_write(&cls->subsys.rwsem); if (error) put_devclass(cls); } @@ -191,7 +193,7 @@ void devclass_remove_device(struct device * dev) if (dev->driver) { cls = dev->driver->devclass; if (cls) { - down_write(&cls->rwsem); + down_write(&cls->subsys.rwsem); pr_debug("device class %s: removing device %s\n", cls->name,dev->name); interface_remove(cls,dev); @@ -202,7 +204,7 @@ void devclass_remove_device(struct device * dev) if (cls->remove_device) cls->remove_device(dev); - up_write(&cls->rwsem); + up_write(&cls->subsys.rwsem); put_devclass(cls); } } @@ -210,34 +212,20 @@ void devclass_remove_device(struct device * dev) struct device_class * get_devclass(struct device_class * cls) { - struct device_class * ret = cls; - spin_lock(&device_lock); - if (cls && cls->present && atomic_read(&cls->refcount) > 0) - atomic_inc(&cls->refcount); - else - ret = NULL; - spin_unlock(&device_lock); - return ret; + return cls ? container_of(subsys_get(&cls->subsys),struct device_class,subsys) : NULL; } void put_devclass(struct device_class * cls) { - if (atomic_dec_and_lock(&cls->refcount,&device_lock)) { - list_del_init(&cls->node); - spin_unlock(&device_lock); - } + subsys_put(&cls->subsys); } int devclass_register(struct device_class * cls) { INIT_LIST_HEAD(&cls->drivers); - INIT_LIST_HEAD(&cls->intf_list); - init_rwsem(&cls->rwsem); - atomic_set(&cls->refcount,2); - cls->present = 1; - pr_debug("device class '%s': registering\n",cls->name); + pr_debug("device class '%s': registering\n",cls->name); strncpy(cls->subsys.kobj.name,cls->name,KOBJ_NAME_LEN); cls->subsys.parent = &class_subsys; subsystem_register(&cls->subsys); @@ -250,23 +238,15 @@ int devclass_register(struct device_class * cls) cls->drvsubsys.parent = &cls->subsys; subsystem_register(&cls->drvsubsys); - spin_lock(&device_lock); - list_add_tail(&cls->node,&class_list); - spin_unlock(&device_lock); - put_devclass(cls); return 0; } void devclass_unregister(struct device_class * cls) { - spin_lock(&device_lock); - cls->present = 0; - spin_unlock(&device_lock); pr_debug("device class '%s': unregistering\n",cls->name); subsystem_unregister(&cls->drvsubsys); subsystem_unregister(&cls->devsubsys); subsystem_unregister(&cls->subsys); - put_devclass(cls); } static int __init class_subsys_init(void) @@ -276,6 +256,8 @@ static int __init class_subsys_init(void) core_initcall(class_subsys_init); +EXPORT_SYMBOL(devclass_create_file); +EXPORT_SYMBOL(devclass_remove_file); EXPORT_SYMBOL(devclass_register); EXPORT_SYMBOL(devclass_unregister); EXPORT_SYMBOL(get_devclass); diff --git a/drivers/base/intf.c b/drivers/base/intf.c index e9eb94a2bed5..d31ad38bf3d2 100644 --- a/drivers/base/intf.c +++ b/drivers/base/intf.c @@ -10,7 +10,7 @@ #include "base.h" -#define to_intf(node) container_of(node,struct device_interface,node) +#define to_intf(node) container_of(node,struct device_interface,kobj.entry) /** * intf_dev_link - symlink from interface's directory to device's directory @@ -34,22 +34,18 @@ static void intf_dev_unlink(struct intf_data * data) int interface_register(struct device_interface * intf) { - struct device_class * cls = intf->devclass; + struct device_class * cls = get_devclass(intf->devclass); + int error = 0; if (cls) { pr_debug("register interface '%s' with class '%s'\n", intf->name,cls->name); - kobject_init(&intf->kobj); strncpy(intf->kobj.name,intf->name,KOBJ_NAME_LEN); intf->kobj.subsys = &cls->subsys; kobject_register(&intf->kobj); - - spin_lock(&device_lock); - list_add_tail(&intf->node,&cls->intf_list); - spin_unlock(&device_lock); - return 0; - } - return -EINVAL; + } else + error = -EINVAL; + return error; } void interface_unregister(struct device_interface * intf) @@ -57,9 +53,6 @@ void interface_unregister(struct device_interface * intf) pr_debug("unregistering interface '%s' from class '%s'\n", intf->name,intf->devclass->name); kobject_unregister(&intf->kobj); - spin_lock(&device_lock); - list_del_init(&intf->node); - spin_unlock(&device_lock); } int interface_add(struct device_class * cls, struct device * dev) @@ -69,7 +62,7 @@ int interface_add(struct device_class * cls, struct device * dev) pr_debug("adding '%s' to %s class interfaces\n",dev->name,cls->name); - list_for_each(node,&cls->intf_list) { + list_for_each(node,&cls->subsys.list) { struct device_interface * intf = to_intf(node); if (intf->add_device) { error = intf->add_device(dev); @@ -89,30 +82,25 @@ void interface_remove(struct device_class * cls, struct device * dev) pr_debug("remove '%s' from %s class interfaces: ",dev->name,cls->name); - spin_lock(&device_lock); list_for_each_safe(node,next,&dev->intf_list) { struct intf_data * intf_data = container_of(node,struct intf_data,node); list_del_init(&intf_data->node); - spin_unlock(&device_lock); intf_dev_unlink(intf_data); pr_debug("%s ",intf_data->intf->name); if (intf_data->intf->remove_device) intf_data->intf->remove_device(intf_data); - - spin_lock(&device_lock); } - spin_unlock(&device_lock); pr_debug("\n"); } int interface_add_data(struct intf_data * data) { - spin_lock(&device_lock); + down_write(&data->intf->devclass->subsys.rwsem); list_add_tail(&data->node,&data->dev->intf_list); - data->intf_num = ++data->intf->devnum; - spin_unlock(&device_lock); + data->intf_num = data->intf->devnum++; intf_dev_link(data); + up_write(&data->intf->devclass->subsys.rwsem); return 0; } diff --git a/include/linux/device.h b/include/linux/device.h index f995ac227140..a88c4c7a3433 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -153,19 +153,12 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute * */ struct device_class { char * name; - struct rw_semaphore rwsem; - - atomic_t refcount; - u32 present; - u32 devnum; struct subsystem subsys; struct subsystem devsubsys; struct subsystem drvsubsys; - struct list_head node; struct list_head drivers; - struct list_head intf_list; int (*add_device)(struct device *); void (*remove_device)(struct device *); @@ -215,7 +208,6 @@ struct device_interface { struct device_class * devclass; struct kobject kobj; - struct list_head node; struct list_head devices; u32 devnum; -- cgit v1.2.3 From 1f7823767c19dc97d88ac899830417c3aa8d825d Mon Sep 17 00:00:00 2001 From: William Lee Irwin III Date: Wed, 20 Nov 2002 19:31:19 -0200 Subject: sched: privatizes the sibling inlines to sched.c, the sole caller of them. --- include/linux/sched.h | 54 --------------------------------------------------- kernel/sched.c | 18 +++++++++++++++++ kernel/signal.c | 30 ++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 9c9717dd1cc1..afc7c0462ccd 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -524,36 +524,6 @@ extern int kill_proc(pid_t, int, int); extern int do_sigaction(int, const struct k_sigaction *, struct k_sigaction *); extern int do_sigaltstack(const stack_t *, stack_t *, unsigned long); -/* - * Re-calculate pending state from the set of locally pending - * signals, globally pending signals, and blocked signals. - */ -static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) -{ - unsigned long ready; - long i; - - switch (_NSIG_WORDS) { - default: - for (i = _NSIG_WORDS, ready = 0; --i >= 0 ;) - ready |= signal->sig[i] &~ blocked->sig[i]; - break; - - case 4: ready = signal->sig[3] &~ blocked->sig[3]; - ready |= signal->sig[2] &~ blocked->sig[2]; - ready |= signal->sig[1] &~ blocked->sig[1]; - ready |= signal->sig[0] &~ blocked->sig[0]; - break; - - case 2: ready = signal->sig[1] &~ blocked->sig[1]; - ready |= signal->sig[0] &~ blocked->sig[0]; - break; - - case 1: ready = signal->sig[0] &~ blocked->sig[0]; - } - return ready != 0; -} - /* True if we are on the alternate signal stack. */ static inline int on_sig_stack(unsigned long sp) @@ -639,30 +609,6 @@ extern void kick_if_running(task_t * p); add_parent(p, (p)->parent); \ } while (0) -static inline struct task_struct *eldest_child(struct task_struct *p) -{ - if (list_empty(&p->children)) return NULL; - return list_entry(p->children.next,struct task_struct,sibling); -} - -static inline struct task_struct *youngest_child(struct task_struct *p) -{ - if (list_empty(&p->children)) return NULL; - return list_entry(p->children.prev,struct task_struct,sibling); -} - -static inline struct task_struct *older_sibling(struct task_struct *p) -{ - if (p->sibling.prev==&p->parent->children) return NULL; - return list_entry(p->sibling.prev,struct task_struct,sibling); -} - -static inline struct task_struct *younger_sibling(struct task_struct *p) -{ - if (p->sibling.next==&p->parent->children) return NULL; - return list_entry(p->sibling.next,struct task_struct,sibling); -} - #define next_task(p) list_entry((p)->tasks.next, struct task_struct, tasks) #define prev_task(p) list_entry((p)->tasks.prev, struct task_struct, tasks) diff --git a/kernel/sched.c b/kernel/sched.c index 35373ad017f5..6d0ac320bcbc 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1837,6 +1837,24 @@ out_unlock: return retval; } +static inline struct task_struct *eldest_child(struct task_struct *p) +{ + if (list_empty(&p->children)) return NULL; + return list_entry(p->children.next,struct task_struct,sibling); +} + +static inline struct task_struct *older_sibling(struct task_struct *p) +{ + if (p->sibling.prev==&p->parent->children) return NULL; + return list_entry(p->sibling.prev,struct task_struct,sibling); +} + +static inline struct task_struct *younger_sibling(struct task_struct *p) +{ + if (p->sibling.next==&p->parent->children) return NULL; + return list_entry(p->sibling.next,struct task_struct,sibling); +} + static void show_task(task_t * p) { unsigned long free = 0; diff --git a/kernel/signal.c b/kernel/signal.c index 961596dd16b2..1379e559bbf4 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -160,6 +160,36 @@ int max_queued_signals = 1024; static int __send_sig_info(int sig, struct siginfo *info, struct task_struct *p); +/* + * Re-calculate pending state from the set of locally pending + * signals, globally pending signals, and blocked signals. + */ +static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) +{ + unsigned long ready; + long i; + + switch (_NSIG_WORDS) { + default: + for (i = _NSIG_WORDS, ready = 0; --i >= 0 ;) + ready |= signal->sig[i] &~ blocked->sig[i]; + break; + + case 4: ready = signal->sig[3] &~ blocked->sig[3]; + ready |= signal->sig[2] &~ blocked->sig[2]; + ready |= signal->sig[1] &~ blocked->sig[1]; + ready |= signal->sig[0] &~ blocked->sig[0]; + break; + + case 2: ready = signal->sig[1] &~ blocked->sig[1]; + ready |= signal->sig[0] &~ blocked->sig[0]; + break; + + case 1: ready = signal->sig[0] &~ blocked->sig[0]; + } + return ready != 0; +} + #define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) void recalc_sigpending_tsk(struct task_struct *t) -- cgit v1.2.3 From fbbe4a70f169cdaebe48fc9bd0d5594605ebb4ac Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 21 Nov 2002 00:04:45 +0000 Subject: [ARM] Fix ARM module support This cset allows ARM modules to work again. The solution was suggested by Andi Kleen. We shrink the available user space size by 16MB, thereby opening up a window in virtual memory space between user space and the kernel direct mapped RAM. We place modules into this space, and, since the kernel image is always at the bottom of kernel direct mapped RAM, we can be assured that any 24-bit PC relocations (which have a range of +/- 32MB) will always be able to reach the kernel. --- arch/arm/kernel/module.c | 79 +++++++++++++++++++++---------- include/asm-arm/arch-adifcc/memory.h | 4 +- include/asm-arm/arch-adifcc/vmalloc.h | 3 ++ include/asm-arm/arch-anakin/memory.h | 4 +- include/asm-arm/arch-anakin/vmalloc.h | 3 ++ include/asm-arm/arch-cl7500/memory.h | 4 +- include/asm-arm/arch-cl7500/vmalloc.h | 4 ++ include/asm-arm/arch-clps711x/memory.h | 4 +- include/asm-arm/arch-clps711x/vmalloc.h | 3 ++ include/asm-arm/arch-ebsa110/memory.h | 4 +- include/asm-arm/arch-ebsa110/vmalloc.h | 3 ++ include/asm-arm/arch-ebsa285/memory.h | 6 +-- include/asm-arm/arch-ebsa285/vmalloc.h | 3 ++ include/asm-arm/arch-epxa10db/memory.h | 4 +- include/asm-arm/arch-epxa10db/vmalloc.h | 3 ++ include/asm-arm/arch-integrator/memory.h | 4 +- include/asm-arm/arch-integrator/vmalloc.h | 3 ++ include/asm-arm/arch-iop310/memory.h | 4 +- include/asm-arm/arch-iop310/vmalloc.h | 3 ++ include/asm-arm/arch-l7200/memory.h | 4 +- include/asm-arm/arch-l7200/vmalloc.h | 3 ++ include/asm-arm/arch-nexuspci/memory.h | 4 +- include/asm-arm/arch-nexuspci/vmalloc.h | 3 ++ include/asm-arm/arch-pxa/memory.h | 4 +- include/asm-arm/arch-pxa/vmalloc.h | 3 ++ include/asm-arm/arch-rpc/memory.h | 4 +- include/asm-arm/arch-rpc/vmalloc.h | 3 ++ include/asm-arm/arch-sa1100/memory.h | 4 +- include/asm-arm/arch-sa1100/vmalloc.h | 3 ++ include/asm-arm/arch-shark/memory.h | 4 +- include/asm-arm/arch-shark/vmalloc.h | 2 + include/asm-arm/arch-tbox/memory.h | 4 +- include/asm-arm/arch-tbox/vmalloc.h | 2 + include/linux/elf.h | 4 ++ 34 files changed, 138 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index 0b2a59f487dc..41ab5fc35443 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c @@ -7,27 +7,59 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * This code is currently broken. We need to allocate a jump table - * for out of range branches. We'd really like to be able to allocate - * a jump table and share it between modules, thereby reducing the - * cache overhead associated with the jump tables. + * Module allocation method suggested by Andi Kleen. */ -#warning FIXME - #include #include #include #include +#include #include #include +#include + void *module_alloc(unsigned long size) { - return NULL; /* disabled */ + struct vm_struct *area; + struct page **pages; + unsigned int array_size, i; + + size = PAGE_ALIGN(size); + if (!size) + goto out_null; + + area = __get_vm_area(size, VM_ALLOC, MODULE_START, MODULE_END); + if (!area) + goto out_null; + + area->nr_pages = size >> PAGE_SHIFT; + array_size = area->nr_pages * sizeof(struct page *); + area->pages = pages = kmalloc(array_size, GFP_KERNEL); + if (!area->pages) { + remove_vm_area(area->addr); + kfree(area); + goto out_null; + } + + memset(pages, 0, array_size); + + for (i = 0; i < area->nr_pages; i++) { + pages[i] = alloc_page(GFP_KERNEL); + if (unlikely(!pages[i])) { + area->nr_pages = i; + goto out_no_pages; + } + } + + if (map_vm_area(area, PAGE_KERNEL, &pages)) + goto out_no_pages; + return area->addr; - if (size == 0) - return NULL; - return vmalloc(size); + out_no_pages: + vfree(area->addr); + out_null: + return NULL; } void module_free(struct module *module, void *region) @@ -59,8 +91,6 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, Elf32_Rel *rel = (void *)relsec->sh_offset; unsigned int i; - printk("Applying relocations for section %u\n", relsec->sh_info); - for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) { unsigned long loc; Elf32_Sym *sym; @@ -81,18 +111,15 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, } if (rel->r_offset < 0 || rel->r_offset > dstsec->sh_size - sizeof(u32)) { - printk(KERN_ERR "%s: out of bounds relocation, section %d reloc %d " - "offset %d size %d\n", - module->name, relindex, i, rel->r_offset, dstsec->sh_size); + printk(KERN_ERR "%s: out of bounds relocation, " + "section %d reloc %d offset %d size %d\n", + module->name, relindex, i, rel->r_offset, + dstsec->sh_size); return -ENOEXEC; } loc = dstsec->sh_offset + rel->r_offset; - printk("%s: rel%d: at 0x%08lx [0x%08lx], symbol '%s' value 0x%08lx =>", - module->name, i, loc, *(u32 *)loc, strtab + sym->st_name, - sym->st_value); - switch (ELF32_R_TYPE(rel->r_info)) { case R_ARM_ABS32: *(u32 *)loc += sym->st_value; @@ -104,9 +131,12 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, offset -= 0x04000000; offset += sym->st_value - loc; - if (offset & 3 || offset <= 0xfc000000 || offset >= 0x04000000) { - printk(KERN_ERR "%s: unable to fixup relocation: out of range\n", - module->name); + if (offset & 3 || + offset <= (s32)0xfc000000 || + offset >= (s32)0x04000000) { + printk(KERN_ERR "%s: unable to fixup " + "relocation: out of range\n", + module->name); return -ENOEXEC; } @@ -117,11 +147,10 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, break; default: - printk("\n" KERN_ERR "%s: unknown relocation: %u\n", - module->name, ELF32_R_TYPE(rel->r_info)); + printk(KERN_ERR "%s: unknown relocation: %u\n", + module->name, ELF32_R_TYPE(rel->r_info)); return -ENOEXEC; } - printk("[0x%08lx]\n", *(u32 *)loc); } return 0; } diff --git a/include/asm-arm/arch-adifcc/memory.h b/include/asm-arm/arch-adifcc/memory.h index 5178f33b2bdf..31b21bc3287d 100644 --- a/include/asm-arm/arch-adifcc/memory.h +++ b/include/asm-arm/arch-adifcc/memory.h @@ -11,14 +11,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-adifcc/vmalloc.h b/include/asm-arm/arch-adifcc/vmalloc.h index 0bcc35c827b3..c97b56b30d97 100644 --- a/include/asm-arm/arch-adifcc/vmalloc.h +++ b/include/asm-arm/arch-adifcc/vmalloc.h @@ -14,3 +14,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (0xe8000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-anakin/memory.h b/include/asm-arm/arch-anakin/memory.h index b58762157b63..554b292c8935 100644 --- a/include/asm-arm/arch-anakin/memory.h +++ b/include/asm-arm/arch-anakin/memory.h @@ -14,9 +14,9 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H -#define TASK_SIZE (3u * 1024 * 1024 * 1024) +#define TASK_SIZE (0xbf000000) #define TASK_SIZE_26 (64u * 1024 * 1024) -#define TASK_UNMAPPED_BASE (1u * 1024 * 1024 * 1024) +#define TASK_UNMAPPED_BASE (0x40000000) #define PAGE_OFFSET 0xc0000000 #define PHYS_OFFSET 0x20000000 diff --git a/include/asm-arm/arch-anakin/vmalloc.h b/include/asm-arm/arch-anakin/vmalloc.h index 1812fbab58e6..913837cc41c9 100644 --- a/include/asm-arm/arch-anakin/vmalloc.h +++ b/include/asm-arm/arch-anakin/vmalloc.h @@ -23,4 +23,7 @@ #define VMALLOC_START ((VMALLOC_VMADDR(high_memory) + VMALLOC_ARCH_OFFSET) & ~(VMALLOC_ARCH_OFFSET - 1)) #define VMALLOC_END (PAGE_OFFSET + 0x10000000) +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) + #endif diff --git a/include/asm-arm/arch-cl7500/memory.h b/include/asm-arm/arch-cl7500/memory.h index 56e1d3bfcb62..b074792de5c9 100644 --- a/include/asm-arm/arch-cl7500/memory.h +++ b/include/asm-arm/arch-cl7500/memory.h @@ -17,14 +17,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-cl7500/vmalloc.h b/include/asm-arm/arch-cl7500/vmalloc.h index 1654e6c3fc01..0e7e4b55270b 100644 --- a/include/asm-arm/arch-cl7500/vmalloc.h +++ b/include/asm-arm/arch-cl7500/vmalloc.h @@ -14,3 +14,7 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x1c000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) + diff --git a/include/asm-arm/arch-clps711x/memory.h b/include/asm-arm/arch-clps711x/memory.h index 52b38ebea23b..317fd2c22dd5 100644 --- a/include/asm-arm/arch-clps711x/memory.h +++ b/include/asm-arm/arch-clps711x/memory.h @@ -25,14 +25,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-clps711x/vmalloc.h b/include/asm-arm/arch-clps711x/vmalloc.h index 5d8324ac39b7..b5d34fcb7ea0 100644 --- a/include/asm-arm/arch-clps711x/vmalloc.h +++ b/include/asm-arm/arch-clps711x/vmalloc.h @@ -30,3 +30,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x10000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-ebsa110/memory.h b/include/asm-arm/arch-ebsa110/memory.h index b8f8e211dac2..146855ded337 100644 --- a/include/asm-arm/arch-ebsa110/memory.h +++ b/include/asm-arm/arch-ebsa110/memory.h @@ -19,14 +19,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-ebsa110/vmalloc.h b/include/asm-arm/arch-ebsa110/vmalloc.h index da319920a2ad..6b6b233de95a 100644 --- a/include/asm-arm/arch-ebsa110/vmalloc.h +++ b/include/asm-arm/arch-ebsa110/vmalloc.h @@ -20,3 +20,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x1f000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-ebsa285/memory.h b/include/asm-arm/arch-ebsa285/memory.h index 41e730ceaf0e..42912ef05551 100644 --- a/include/asm-arm/arch-ebsa285/memory.h +++ b/include/asm-arm/arch-ebsa285/memory.h @@ -48,13 +48,13 @@ extern unsigned long __bus_to_virt(unsigned long); #if defined(CONFIG_ARCH_FOOTBRIDGE) /* Task size and page offset at 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define PAGE_OFFSET (0xc0000000UL) #elif defined(CONFIG_ARCH_CO285) /* Task size and page offset at 1.5GB */ -#define TASK_SIZE (0x60000000UL) +#define TASK_SIZE (0x5f000000UL) #define PAGE_OFFSET (0x60000000UL) #else @@ -70,7 +70,7 @@ extern unsigned long __bus_to_virt(unsigned long); * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE ((TASK_SIZE + 0x01000000) / 3) /* * The DRAM is always contiguous. diff --git a/include/asm-arm/arch-ebsa285/vmalloc.h b/include/asm-arm/arch-ebsa285/vmalloc.h index c66b7fa88da8..31b2c6715620 100644 --- a/include/asm-arm/arch-ebsa285/vmalloc.h +++ b/include/asm-arm/arch-ebsa285/vmalloc.h @@ -25,3 +25,6 @@ #else #define VMALLOC_END (PAGE_OFFSET + 0x20000000) #endif + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-epxa10db/memory.h b/include/asm-arm/arch-epxa10db/memory.h index 132eab6ad9d0..3af88af12e74 100644 --- a/include/asm-arm/arch-epxa10db/memory.h +++ b/include/asm-arm/arch-epxa10db/memory.h @@ -23,14 +23,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-epxa10db/vmalloc.h b/include/asm-arm/arch-epxa10db/vmalloc.h index 71b1a6229cb4..d6961cf75699 100644 --- a/include/asm-arm/arch-epxa10db/vmalloc.h +++ b/include/asm-arm/arch-epxa10db/vmalloc.h @@ -30,3 +30,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x10000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-integrator/memory.h b/include/asm-arm/arch-integrator/memory.h index 45605011117c..25df3f54107a 100644 --- a/include/asm-arm/arch-integrator/memory.h +++ b/include/asm-arm/arch-integrator/memory.h @@ -23,14 +23,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-integrator/vmalloc.h b/include/asm-arm/arch-integrator/vmalloc.h index ec8f98d3fa5b..41b5a1b84b11 100644 --- a/include/asm-arm/arch-integrator/vmalloc.h +++ b/include/asm-arm/arch-integrator/vmalloc.h @@ -30,3 +30,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x10000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-iop310/memory.h b/include/asm-arm/arch-iop310/memory.h index e413a04823a9..e61bc5ddc48a 100644 --- a/include/asm-arm/arch-iop310/memory.h +++ b/include/asm-arm/arch-iop310/memory.h @@ -10,14 +10,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-iop310/vmalloc.h b/include/asm-arm/arch-iop310/vmalloc.h index 07c8ee703750..0b481a541443 100644 --- a/include/asm-arm/arch-iop310/vmalloc.h +++ b/include/asm-arm/arch-iop310/vmalloc.h @@ -14,3 +14,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (0xe8000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-l7200/memory.h b/include/asm-arm/arch-l7200/memory.h index a34a0f9a41bb..12f8ad8c25ba 100644 --- a/include/asm-arm/arch-l7200/memory.h +++ b/include/asm-arm/arch-l7200/memory.h @@ -15,14 +15,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-l7200/vmalloc.h b/include/asm-arm/arch-l7200/vmalloc.h index 04fa07e7cbf5..7c19757c1b00 100644 --- a/include/asm-arm/arch-l7200/vmalloc.h +++ b/include/asm-arm/arch-l7200/vmalloc.h @@ -14,3 +14,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x10000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-nexuspci/memory.h b/include/asm-arm/arch-nexuspci/memory.h index a7e644257805..89423f082126 100644 --- a/include/asm-arm/arch-nexuspci/memory.h +++ b/include/asm-arm/arch-nexuspci/memory.h @@ -11,14 +11,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-nexuspci/vmalloc.h b/include/asm-arm/arch-nexuspci/vmalloc.h index 5c8d17dd7f5b..877a164f5668 100644 --- a/include/asm-arm/arch-nexuspci/vmalloc.h +++ b/include/asm-arm/arch-nexuspci/vmalloc.h @@ -14,3 +14,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x20000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-pxa/memory.h b/include/asm-arm/arch-pxa/memory.h index 24e133caf425..65cc8c434e6a 100644 --- a/include/asm-arm/arch-pxa/memory.h +++ b/include/asm-arm/arch-pxa/memory.h @@ -16,14 +16,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-pxa/vmalloc.h b/include/asm-arm/arch-pxa/vmalloc.h index a75e7b7593ea..a3a19b8100c4 100644 --- a/include/asm-arm/arch-pxa/vmalloc.h +++ b/include/asm-arm/arch-pxa/vmalloc.h @@ -21,3 +21,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (0xe8000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-rpc/memory.h b/include/asm-arm/arch-rpc/memory.h index cf562f87f7b5..c6c147ef6685 100644 --- a/include/asm-arm/arch-rpc/memory.h +++ b/include/asm-arm/arch-rpc/memory.h @@ -21,14 +21,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-rpc/vmalloc.h b/include/asm-arm/arch-rpc/vmalloc.h index 579bd11904af..f47bed41b2bb 100644 --- a/include/asm-arm/arch-rpc/vmalloc.h +++ b/include/asm-arm/arch-rpc/vmalloc.h @@ -20,3 +20,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x1c000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-sa1100/memory.h b/include/asm-arm/arch-sa1100/memory.h index 1f7c463b32b1..04df58feeabc 100644 --- a/include/asm-arm/arch-sa1100/memory.h +++ b/include/asm-arm/arch-sa1100/memory.h @@ -12,14 +12,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-sa1100/vmalloc.h b/include/asm-arm/arch-sa1100/vmalloc.h index 82c5ce9b6acc..35c2f3fe740e 100644 --- a/include/asm-arm/arch-sa1100/vmalloc.h +++ b/include/asm-arm/arch-sa1100/vmalloc.h @@ -14,3 +14,6 @@ #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (0xe8000000) + +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-shark/memory.h b/include/asm-arm/arch-shark/memory.h index 39f167176011..47dc994b8b2d 100644 --- a/include/asm-arm/arch-shark/memory.h +++ b/include/asm-arm/arch-shark/memory.h @@ -13,14 +13,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: = 3GB diff --git a/include/asm-arm/arch-shark/vmalloc.h b/include/asm-arm/arch-shark/vmalloc.h index 31fe3e777083..8a45e9d954c6 100644 --- a/include/asm-arm/arch-shark/vmalloc.h +++ b/include/asm-arm/arch-shark/vmalloc.h @@ -15,3 +15,5 @@ #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x10000000) +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/asm-arm/arch-tbox/memory.h b/include/asm-arm/arch-tbox/memory.h index e93d0afb176f..7fc1c3032e30 100644 --- a/include/asm-arm/arch-tbox/memory.h +++ b/include/asm-arm/arch-tbox/memory.h @@ -10,14 +10,14 @@ /* * Task size: 3GB */ -#define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE (0xbf000000UL) #define TASK_SIZE_26 (0x04000000UL) /* * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (0x40000000) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-tbox/vmalloc.h b/include/asm-arm/arch-tbox/vmalloc.h index c2e21d5dedd6..c620d349a698 100644 --- a/include/asm-arm/arch-tbox/vmalloc.h +++ b/include/asm-arm/arch-tbox/vmalloc.h @@ -15,3 +15,5 @@ #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (PAGE_OFFSET + 0x10000000) +#define MODULE_START (PAGE_OFFSET - 16*1048576) +#define MODULE_END (PAGE_OFFSET) diff --git a/include/linux/elf.h b/include/linux/elf.h index 0fc8629ac459..04a7ae9a30a2 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -200,6 +200,10 @@ typedef struct { #define ELF64_R_SYM(i) ((i) >> 32) #define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define R_ARM_NONE 0 +#define R_ARM_PC24 1 +#define R_ARM_ABS32 2 + #define R_386_NONE 0 #define R_386_32 1 #define R_386_PC32 2 -- cgit v1.2.3 From dbc4fc9c16d84f2c6808d1dc436358db0ea802b4 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 20 Nov 2002 20:25:03 -0800 Subject: [PATCH] threading enhancements, tid-2.5.48-C0 Support more flexible child pid set/clear operations for NPTL. there's one more improvement in the interface: set the parent-TID prior doing the copy_mm() - this helps cfork() to pass the TID to the child as well. --- arch/i386/kernel/entry.S | 2 -- arch/i386/kernel/process.c | 15 ++++++++------- arch/i386/kernel/smpboot.c | 2 +- include/linux/sched.h | 14 ++++++++------ kernel/fork.c | 42 ++++++++++++++++++++++-------------------- kernel/sched.c | 4 ++-- 6 files changed, 41 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 830c84025de1..6a9b0de4421c 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -170,10 +170,8 @@ ENTRY(lcall27) ENTRY(ret_from_fork) -#if CONFIG_SMP || CONFIG_PREEMPT # NOTE: this function takes a parameter but it's unused on x86. call schedule_tail -#endif GET_THREAD_INFO(%ebx) jmp syscall_exit diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index d6640642ae3d..253f08d92da3 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -226,7 +226,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) regs.eflags = 0x286; /* Ok, create the new process.. */ - p = do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL); + p = do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); return IS_ERR(p) ? PTR_ERR(p) : p->pid; } @@ -288,7 +288,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, struct_cpy(childregs, regs); childregs->eax = 0; childregs->esp = esp; - p->user_tid = NULL; + p->set_child_tid = p->clear_child_tid = NULL; p->thread.esp = (unsigned long) childregs; p->thread.esp0 = (unsigned long) (childregs+1); @@ -503,7 +503,7 @@ asmlinkage int sys_fork(struct pt_regs regs) { struct task_struct *p; - p = do_fork(SIGCHLD, regs.esp, ®s, 0, NULL); + p = do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL); return IS_ERR(p) ? PTR_ERR(p) : p->pid; } @@ -512,14 +512,15 @@ asmlinkage int sys_clone(struct pt_regs regs) struct task_struct *p; unsigned long clone_flags; unsigned long newsp; - int *user_tid; + int *parent_tidptr, *child_tidptr; clone_flags = regs.ebx; newsp = regs.ecx; - user_tid = (int *)regs.edx; + parent_tidptr = (int *)regs.edx; + child_tidptr = (int *)regs.edi; if (!newsp) newsp = regs.esp; - p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, user_tid); + p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, parent_tidptr, child_tidptr); return IS_ERR(p) ? PTR_ERR(p) : p->pid; } @@ -537,7 +538,7 @@ asmlinkage int sys_vfork(struct pt_regs regs) { struct task_struct *p; - p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL); + p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL); return IS_ERR(p) ? PTR_ERR(p) : p->pid; } diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 876e95fd57ce..66c93c67d7db 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -499,7 +499,7 @@ static struct task_struct * __init fork_by_hand(void) * don't care about the eip and regs settings since * we'll never reschedule the forked task. */ - return do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL); + return do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); } /* which physical APIC ID maps to which logical CPU number */ diff --git a/include/linux/sched.h b/include/linux/sched.h index afc7c0462ccd..7946bd8cb0ad 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -46,10 +46,11 @@ struct exec_domain; #define CLONE_NEWNS 0x00020000 /* New namespace group? */ #define CLONE_SYSVSEM 0x00040000 /* share system V SEM_UNDO semantics */ #define CLONE_SETTLS 0x00080000 /* create a new TLS for the child */ -#define CLONE_SETTID 0x00100000 /* write the TID back to userspace */ -#define CLONE_CLEARTID 0x00200000 /* clear the userspace TID */ -#define CLONE_DETACHED 0x00400000 /* parent wants no child-exit signal */ -#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */ +#define CLONE_PARENT_SETTID 0x00100000 /* set the TID in the parent */ +#define CLONE_CHILD_CLEARTID 0x00200000 /* clear the TID in the child */ +#define CLONE_DETACHED 0x00400000 /* parent wants no child-exit signal */ +#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */ +#define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */ /* * List of flags we want to share for kernel threads, @@ -332,7 +333,8 @@ struct task_struct { wait_queue_head_t wait_chldexit; /* for wait4() */ struct completion *vfork_done; /* for vfork() */ - int *user_tid; /* for CLONE_CLEARTID */ + int *set_child_tid; /* CLONE_CHILD_SETTID */ + int *clear_child_tid; /* CLONE_CHILD_CLEARTID */ unsigned long rt_priority; unsigned long it_real_value, it_prof_value, it_virt_value; @@ -585,7 +587,7 @@ extern void daemonize(void); extern task_t *child_reaper; extern int do_execve(char *, char **, char **, struct pt_regs *); -extern struct task_struct *do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int *); +extern struct task_struct *do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int *, int *); #ifdef CONFIG_SMP extern void wait_task_inactive(task_t * p); diff --git a/kernel/fork.c b/kernel/fork.c index f89734b831ac..27cf572cedf8 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -408,16 +408,16 @@ void mm_release(void) tsk->vfork_done = NULL; complete(vfork_done); } - if (tsk->user_tid) { - int * user_tid = tsk->user_tid; - tsk->user_tid = NULL; + if (tsk->clear_child_tid) { + int * tidptr = tsk->clear_child_tid; + tsk->clear_child_tid = NULL; /* * We dont check the error code - if userspace has * not set up a proper pointer then tough luck. */ - put_user(0, user_tid); - sys_futex((unsigned long)user_tid, FUTEX_WAKE, 1, NULL); + put_user(0, tidptr); + sys_futex((unsigned long)tidptr, FUTEX_WAKE, 1, NULL); } } @@ -680,9 +680,9 @@ static inline void copy_flags(unsigned long clone_flags, struct task_struct *p) p->flags = new_flags; } -asmlinkage int sys_set_tid_address(int *user_tid) +asmlinkage int sys_set_tid_address(int *tidptr) { - current->user_tid = user_tid; + current->clear_child_tid = tidptr; return current->pid; } @@ -699,7 +699,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, - int *user_tid) + int *parent_tidptr, + int *child_tidptr) { int retval; struct task_struct *p = NULL; @@ -766,6 +767,11 @@ static struct task_struct *copy_process(unsigned long clone_flags, if (p->pid == -1) goto bad_fork_cleanup; } + retval = -EFAULT; + if (clone_flags & CLONE_PARENT_SETTID) + if (put_user(p->pid, parent_tidptr)) + goto bad_fork_cleanup; + p->proc_dentry = NULL; INIT_LIST_HEAD(&p->run_list); @@ -823,19 +829,14 @@ static struct task_struct *copy_process(unsigned long clone_flags, retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); if (retval) goto bad_fork_cleanup_namespace; - /* - * Notify the child of the TID? - */ - retval = -EFAULT; - if (clone_flags & CLONE_SETTID) - if (put_user(p->pid, user_tid)) - goto bad_fork_cleanup_namespace; + if (clone_flags & CLONE_CHILD_SETTID) + p->set_child_tid = child_tidptr; /* - * Does the userspace VM want the TID cleared on mm_release()? + * Clear TID on mm_release()? */ - if (clone_flags & CLONE_CLEARTID) - p->user_tid = user_tid; + if (clone_flags & CLONE_CHILD_CLEARTID) + p->clear_child_tid = child_tidptr; /* * Syscall tracing should be turned off in the child regardless @@ -1004,7 +1005,8 @@ struct task_struct *do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, - int *user_tid) + int *parent_tidptr, + int *child_tidptr) { struct task_struct *p; int trace = 0; @@ -1015,7 +1017,7 @@ struct task_struct *do_fork(unsigned long clone_flags, clone_flags |= CLONE_PTRACE; } - p = copy_process(clone_flags, stack_start, regs, stack_size, user_tid); + p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr); if (!IS_ERR(p)) { struct completion vfork; diff --git a/kernel/sched.c b/kernel/sched.c index 6d0ac320bcbc..b446ce8eecc1 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -503,12 +503,12 @@ void sched_exit(task_t * p) * schedule_tail - first thing a freshly forked thread must call. * @prev: the thread we just switched away from. */ -#if CONFIG_SMP || CONFIG_PREEMPT asmlinkage void schedule_tail(task_t *prev) { finish_arch_switch(this_rq(), prev); + if (current->set_child_tid) + put_user(current->pid, current->set_child_tid); } -#endif /* * context_switch - switch to the new MM and the new -- cgit v1.2.3 From 4eccc58aa38d06afb9ea22e44bfdcd04e3dd57ff Mon Sep 17 00:00:00 2001 From: Nicolas Mailhot Date: Wed, 20 Nov 2002 22:25:51 -0800 Subject: [PATCH] Via KT400 agp support This adds the KT400 pci ID and lists it as using Via generic setup routines. This patch has been tested with all GL xscreensavers I could find, and been reviewed by Dave Jones (full patch history at http://bugzilla.kernel.org/show_bug.cgi?id=3D14). diff -uNr linux-2.5.47-ac6.orig/drivers/char/agp/agp.c linux-2.5.47-ac6/drivers/char/agp/agp.c --- drivers/char/agp/agp.c | 8 ++++++++ include/linux/agp_backend.h | 1 + include/linux/pci_ids.h | 1 + 3 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/drivers/char/agp/agp.c b/drivers/char/agp/agp.c index 6130ff20c1e7..d2034e6bceb7 100644 --- a/drivers/char/agp/agp.c +++ b/drivers/char/agp/agp.c @@ -1149,6 +1149,14 @@ static struct { .chipset_name = "Apollo Pro KT266", .chipset_setup = via_generic_setup }, + { + .device_id = PCI_DEVICE_ID_VIA_8377_0, + .vendor_id = PCI_VENDOR_ID_VIA, + .chipset = VIA_APOLLO_KT400, + .vendor_name = "Via", + .chipset_name = "Apollo Pro KT400", + .chipset_setup = via_generic_setup + }, { .device_id = PCI_DEVICE_ID_VIA_8653_0, .vendor_id = PCI_VENDOR_ID_VIA, diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h index f250d4da587c..ad1d8a935d21 100644 --- a/include/linux/agp_backend.h +++ b/include/linux/agp_backend.h @@ -61,6 +61,7 @@ enum chipset_type { VIA_APOLLO_PRO, VIA_APOLLO_KX133, VIA_APOLLO_KT133, + VIA_APOLLO_KT400, SIS_GENERIC, AMD_GENERIC, AMD_IRONGATE, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 44c84141a6b4..f8890c70fa89 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1018,6 +1018,7 @@ #define PCI_DEVICE_ID_VIA_8361 0x3112 #define PCI_DEVICE_ID_VIA_8233A 0x3147 #define PCI_DEVICE_ID_VIA_8235 0x3177 +#define PCI_DEVICE_ID_VIA_8377_0 0x3189 #define PCI_DEVICE_ID_VIA_86C100A 0x6100 #define PCI_DEVICE_ID_VIA_8231 0x8231 #define PCI_DEVICE_ID_VIA_8231_4 0x8235 -- cgit v1.2.3 From 3408a276c58dba9b8c5fe035ead630294cd8e790 Mon Sep 17 00:00:00 2001 From: Brian Gerst Date: Wed, 20 Nov 2002 22:52:04 -0800 Subject: [PATCH] break up fs/devices.c This patch breaks up and removes fs/devices.c, moving functions to more logical places. character device functions -> char_dev.c init_special_inode() -> inode.c kdevname() -> libfs.c (this should die, but that's another patch) bad_sock_fops -> socket.c --- fs/Makefile | 2 +- fs/char_dev.c | 164 ++++++++++++++++++++++++++++++++++++++ fs/devices.c | 222 ---------------------------------------------------- fs/inode.c | 18 +++++ fs/libfs.c | 11 +++ fs/proc/proc_misc.c | 6 +- include/linux/fs.h | 2 + net/socket.c | 4 + 8 files changed, 204 insertions(+), 225 deletions(-) delete mode 100644 fs/devices.c (limited to 'include/linux') diff --git a/fs/Makefile b/fs/Makefile index 963b38e3108f..0732416467c2 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -8,7 +8,7 @@ export-objs := open.o dcache.o buffer.o bio.o inode.o dquot.o mpage.o aio.o \ fcntl.o read_write.o dcookies.o mbcache.o posix_acl.o xattr_acl.o -obj-y := open.o read_write.o devices.o file_table.o buffer.o \ +obj-y := open.o read_write.o file_table.o buffer.o \ bio.o super.o block_dev.o char_dev.o stat.o exec.o pipe.o \ namei.o fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \ dcache.o inode.o attr.o bad_inode.o file.o dnotify.o \ diff --git a/fs/char_dev.c b/fs/char_dev.c index bacc67cfdda1..ff34b5e336cd 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -10,6 +10,23 @@ #include #include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KMOD +#include +#include + +/* serial module kmod load support */ +struct tty_driver *get_tty_driver(kdev_t device); +#define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR) +#define need_serial(ma,mi) (get_tty_driver(mk_kdev(ma,mi)) == NULL) +#endif + #define HASH_BITS 6 #define HASH_SIZE (1UL << HASH_BITS) #define HASH_MASK (HASH_SIZE-1) @@ -113,3 +130,150 @@ void cdput(struct char_device *cdev) } } +struct device_struct { + const char * name; + struct file_operations * fops; +}; + +static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED; +static struct device_struct chrdevs[MAX_CHRDEV]; + +int get_chrdev_list(char *page) +{ + int i; + int len; + + len = sprintf(page, "Character devices:\n"); + read_lock(&chrdevs_lock); + for (i = 0; i < MAX_CHRDEV ; i++) { + if (chrdevs[i].fops) { + len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name); + } + } + read_unlock(&chrdevs_lock); + return len; +} + +/* + Return the function table of a device. + Load the driver if needed. + Increment the reference count of module in question. +*/ +static struct file_operations * get_chrfops(unsigned int major, unsigned int minor) +{ + struct file_operations *ret = NULL; + + if (!major || major >= MAX_CHRDEV) + return NULL; + + read_lock(&chrdevs_lock); + ret = fops_get(chrdevs[major].fops); + read_unlock(&chrdevs_lock); +#ifdef CONFIG_KMOD + if (ret && isa_tty_dev(major)) { + lock_kernel(); + if (need_serial(major,minor)) { + /* Force request_module anyway, but what for? */ + fops_put(ret); + ret = NULL; + } + unlock_kernel(); + } + if (!ret) { + char name[20]; + sprintf(name, "char-major-%d", major); + request_module(name); + + read_lock(&chrdevs_lock); + ret = fops_get(chrdevs[major].fops); + read_unlock(&chrdevs_lock); + } +#endif + return ret; +} + +int register_chrdev(unsigned int major, const char * name, struct file_operations *fops) +{ + if (devfs_only()) + return 0; + if (major == 0) { + write_lock(&chrdevs_lock); + for (major = MAX_CHRDEV-1; major > 0; major--) { + if (chrdevs[major].fops == NULL) { + chrdevs[major].name = name; + chrdevs[major].fops = fops; + write_unlock(&chrdevs_lock); + return major; + } + } + write_unlock(&chrdevs_lock); + return -EBUSY; + } + if (major >= MAX_CHRDEV) + return -EINVAL; + write_lock(&chrdevs_lock); + if (chrdevs[major].fops && chrdevs[major].fops != fops) { + write_unlock(&chrdevs_lock); + return -EBUSY; + } + chrdevs[major].name = name; + chrdevs[major].fops = fops; + write_unlock(&chrdevs_lock); + return 0; +} + +int unregister_chrdev(unsigned int major, const char * name) +{ + if (devfs_only()) + return 0; + if (major >= MAX_CHRDEV) + return -EINVAL; + write_lock(&chrdevs_lock); + if (!chrdevs[major].fops || strcmp(chrdevs[major].name, name)) { + write_unlock(&chrdevs_lock); + return -EINVAL; + } + chrdevs[major].name = NULL; + chrdevs[major].fops = NULL; + write_unlock(&chrdevs_lock); + return 0; +} + +/* + * Called every time a character special file is opened + */ +int chrdev_open(struct inode * inode, struct file * filp) +{ + int ret = -ENODEV; + + filp->f_op = get_chrfops(major(inode->i_rdev), minor(inode->i_rdev)); + if (filp->f_op) { + ret = 0; + if (filp->f_op->open != NULL) { + lock_kernel(); + ret = filp->f_op->open(inode,filp); + unlock_kernel(); + } + } + return ret; +} + +/* + * Dummy default file-operations: the only thing this does + * is contain the open that then fills in the correct operations + * depending on the special file... + */ +struct file_operations def_chr_fops = { + .open = chrdev_open, +}; + +const char * cdevname(kdev_t dev) +{ + static char buffer[32]; + const char * name = chrdevs[major(dev)].name; + + if (!name) + name = "unknown-char"; + sprintf(buffer, "%s(%d,%d)", name, major(dev), minor(dev)); + return buffer; +} diff --git a/fs/devices.c b/fs/devices.c deleted file mode 100644 index adf05053eeb7..000000000000 --- a/fs/devices.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * linux/fs/devices.c - * - * (C) 1993 Matthias Urlichs -- collected common code and tables. - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * Added kerneld support: Jacques Gelinas and Bjorn Ekwall - * (changed to kmod) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_KMOD -#include - -#include - -/* serial module kmod load support */ -struct tty_driver *get_tty_driver(kdev_t device); -#define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR) -#define need_serial(ma,mi) (get_tty_driver(mk_kdev(ma,mi)) == NULL) -#endif - -struct device_struct { - const char * name; - struct file_operations * fops; -}; - -static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED; -static struct device_struct chrdevs[MAX_CHRDEV]; - -extern int get_blkdev_list(char *); - -int get_device_list(char * page) -{ - int i; - int len; - - len = sprintf(page, "Character devices:\n"); - read_lock(&chrdevs_lock); - for (i = 0; i < MAX_CHRDEV ; i++) { - if (chrdevs[i].fops) { - len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name); - } - } - read_unlock(&chrdevs_lock); - len += get_blkdev_list(page+len); - return len; -} - -/* - Return the function table of a device. - Load the driver if needed. - Increment the reference count of module in question. -*/ -static struct file_operations * get_chrfops(unsigned int major, unsigned int minor) -{ - struct file_operations *ret = NULL; - - if (!major || major >= MAX_CHRDEV) - return NULL; - - read_lock(&chrdevs_lock); - ret = fops_get(chrdevs[major].fops); - read_unlock(&chrdevs_lock); -#ifdef CONFIG_KMOD - if (ret && isa_tty_dev(major)) { - lock_kernel(); - if (need_serial(major,minor)) { - /* Force request_module anyway, but what for? */ - fops_put(ret); - ret = NULL; - } - unlock_kernel(); - } - if (!ret) { - char name[20]; - sprintf(name, "char-major-%d", major); - request_module(name); - - read_lock(&chrdevs_lock); - ret = fops_get(chrdevs[major].fops); - read_unlock(&chrdevs_lock); - } -#endif - return ret; -} - -int register_chrdev(unsigned int major, const char * name, struct file_operations *fops) -{ - if (devfs_only()) - return 0; - if (major == 0) { - write_lock(&chrdevs_lock); - for (major = MAX_CHRDEV-1; major > 0; major--) { - if (chrdevs[major].fops == NULL) { - chrdevs[major].name = name; - chrdevs[major].fops = fops; - write_unlock(&chrdevs_lock); - return major; - } - } - write_unlock(&chrdevs_lock); - return -EBUSY; - } - if (major >= MAX_CHRDEV) - return -EINVAL; - write_lock(&chrdevs_lock); - if (chrdevs[major].fops && chrdevs[major].fops != fops) { - write_unlock(&chrdevs_lock); - return -EBUSY; - } - chrdevs[major].name = name; - chrdevs[major].fops = fops; - write_unlock(&chrdevs_lock); - return 0; -} - -int unregister_chrdev(unsigned int major, const char * name) -{ - if (devfs_only()) - return 0; - if (major >= MAX_CHRDEV) - return -EINVAL; - write_lock(&chrdevs_lock); - if (!chrdevs[major].fops || strcmp(chrdevs[major].name, name)) { - write_unlock(&chrdevs_lock); - return -EINVAL; - } - chrdevs[major].name = NULL; - chrdevs[major].fops = NULL; - write_unlock(&chrdevs_lock); - return 0; -} - -/* - * Called every time a character special file is opened - */ -int chrdev_open(struct inode * inode, struct file * filp) -{ - int ret = -ENODEV; - - filp->f_op = get_chrfops(major(inode->i_rdev), minor(inode->i_rdev)); - if (filp->f_op) { - ret = 0; - if (filp->f_op->open != NULL) { - lock_kernel(); - ret = filp->f_op->open(inode,filp); - unlock_kernel(); - } - } - return ret; -} - -/* - * Dummy default file-operations: the only thing this does - * is contain the open that then fills in the correct operations - * depending on the special file... - */ -static struct file_operations def_chr_fops = { - .open = chrdev_open, -}; - -/* - * Print device name (in decimal, hexadecimal or symbolic) - * Note: returns pointer to static data! - */ -const char * kdevname(kdev_t dev) -{ - static char buffer[32]; - sprintf(buffer, "%02x:%02x", major(dev), minor(dev)); - return buffer; -} - -const char * cdevname(kdev_t dev) -{ - static char buffer[32]; - const char * name = chrdevs[major(dev)].name; - - if (!name) - name = "unknown-char"; - sprintf(buffer, "%s(%d,%d)", name, major(dev), minor(dev)); - return buffer; -} - -static int sock_no_open(struct inode *irrelevant, struct file *dontcare) -{ - return -ENXIO; -} - -static struct file_operations bad_sock_fops = { - .open = sock_no_open -}; - -void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) -{ - inode->i_mode = mode; - if (S_ISCHR(mode)) { - inode->i_fop = &def_chr_fops; - inode->i_rdev = to_kdev_t(rdev); - inode->i_cdev = cdget(rdev); - } else if (S_ISBLK(mode)) { - inode->i_fop = &def_blk_fops; - inode->i_rdev = to_kdev_t(rdev); - } else if (S_ISFIFO(mode)) - inode->i_fop = &def_fifo_fops; - else if (S_ISSOCK(mode)) - inode->i_fop = &bad_sock_fops; - else - printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", - mode); -} diff --git a/fs/inode.c b/fs/inode.c index 1cb734ec9589..c14cc396654e 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1270,3 +1270,21 @@ void __init inode_init(unsigned long mempages) set_shrinker(DEFAULT_SEEKS, shrink_icache_memory); } + +void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) +{ + inode->i_mode = mode; + if (S_ISCHR(mode)) { + inode->i_fop = &def_chr_fops; + inode->i_rdev = to_kdev_t(rdev); + inode->i_cdev = cdget(rdev); + } else if (S_ISBLK(mode)) { + inode->i_fop = &def_blk_fops; + inode->i_rdev = to_kdev_t(rdev); + } else if (S_ISFIFO(mode)) + inode->i_fop = &def_fifo_fops; + else if (S_ISSOCK(mode)) + inode->i_fop = &bad_sock_fops; + else + printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o)\n", mode); +} diff --git a/fs/libfs.c b/fs/libfs.c index 07ab22e1c256..813bc9046bcd 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -323,3 +323,14 @@ int simple_commit_write(struct file *file, struct page *page, set_page_dirty(page); return 0; } + +/* + * Print device name (in decimal, hexadecimal or symbolic) + * Note: returns pointer to static data! + */ +const char * kdevname(kdev_t dev) +{ + static char buffer[32]; + sprintf(buffer, "%02x:%02x", major(dev), minor(dev)); + return buffer; +} diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index fcc79c979b39..ba7f6659b203 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -57,7 +57,8 @@ */ extern int get_hardware_list(char *); extern int get_stram_list(char *); -extern int get_device_list(char *); +extern int get_chrdev_list(char *); +extern int get_blkdev_list(char *); extern int get_filesystem_list(char *); extern int get_exec_domain_list(char *); extern int get_dma_list(char *); @@ -376,7 +377,8 @@ static int kstat_read_proc(char *page, char **start, off_t off, static int devices_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { - int len = get_device_list(page); + int len = get_chrdev_list(page); + len += get_blkdev_list(page+len); return proc_calc_metrics(page, start, off, count, eof, len); } diff --git a/include/linux/fs.h b/include/linux/fs.h index bbb4e2cacecc..7234a60f85a5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1089,6 +1089,8 @@ extern int blkdev_open(struct inode *, struct file *); extern int blkdev_close(struct inode *, struct file *); extern struct file_operations def_blk_fops; extern struct address_space_operations def_blk_aops; +extern struct file_operations def_chr_fops; +extern struct file_operations bad_sock_fops; extern struct file_operations def_fifo_fops; extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long); extern int blkdev_ioctl(struct inode *, struct file *, unsigned, unsigned long); diff --git a/net/socket.c b/net/socket.c index f46e465d5fa2..448ed2c395a3 100644 --- a/net/socket.c +++ b/net/socket.c @@ -491,6 +491,10 @@ static int sock_no_open(struct inode *irrelevant, struct file *dontcare) return -ENXIO; } +struct file_operations bad_sock_fops = { + .open = sock_no_open, +}; + /** * sock_release - close a socket * @sock: socket to close -- cgit v1.2.3 From 47de5842a7dc89aab8b96c721c5d8dd6cc3ab393 Mon Sep 17 00:00:00 2001 From: "Andries E. Brouwer" Date: Wed, 20 Nov 2002 23:48:53 -0800 Subject: [PATCH] kill i_dev The i_dev field is deleted and the few uses are replaced by i_sb->s_dev. There is a single side effect: a stat on a socket now sees a nonzero st_dev. There is nothing against that - FreeBSD has a nonzero value as well - but there is at least one utility (fuser) that will need an update. --- fs/coda/coda_linux.c | 2 +- fs/inode.c | 1 - fs/intermezzo/presto.c | 2 +- fs/intermezzo/vfs.c | 8 ++++---- fs/locks.c | 2 +- fs/nfsd/nfs3xdr.c | 2 +- fs/nfsd/vfs.c | 6 +++--- fs/proc/array.c | 5 +++-- fs/stat.c | 2 +- fs/udf/inode.c | 1 - fs/xfs/xfs_iget.c | 4 ++-- fs/xfs/xfsidbg.c | 2 +- include/linux/fs.h | 1 - net/socket.c | 1 - 14 files changed, 18 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index 2d8cd35cca04..cf71476eeed1 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -100,7 +100,7 @@ unsigned short coda_flags_to_cflags(unsigned short flags) void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr) { int inode_type; - /* inode's i_dev, i_flags, i_ino are set by iget + /* inode's i_flags, i_ino are set by iget XXX: is this all we need ?? */ switch (attr->va_type) { diff --git a/fs/inode.c b/fs/inode.c index c14cc396654e..acd4421c5890 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -102,7 +102,6 @@ static struct inode *alloc_inode(struct super_block *sb) struct address_space * const mapping = &inode->i_data; inode->i_sb = sb; - inode->i_dev = sb->s_dev; inode->i_blkbits = sb->s_blocksize_bits; inode->i_flags = 0; atomic_set(&inode->i_count, 1); diff --git a/fs/intermezzo/presto.c b/fs/intermezzo/presto.c index 006d08e94ee2..8094540c7aae 100644 --- a/fs/intermezzo/presto.c +++ b/fs/intermezzo/presto.c @@ -67,7 +67,7 @@ int presto_i2m(struct inode *inode) CDEBUG(D_PSDEV, "\n"); if ( !cache ) { CERROR("PRESTO: BAD: cannot find cache for dev %d, ino %ld\n", - inode->i_dev, inode->i_ino); + inode->i_sb->s_dev, inode->i_ino); EXIT; return -1; } diff --git a/fs/intermezzo/vfs.c b/fs/intermezzo/vfs.c index fb3791b4a82a..1bd1240975cf 100644 --- a/fs/intermezzo/vfs.c +++ b/fs/intermezzo/vfs.c @@ -182,7 +182,7 @@ inline void presto_debug_fail_blkdev(struct presto_file_set *fset, { int minor = presto_f2m(fset); int errorval = izo_channels[minor].uc_errorval; - kdev_t dev = to_kdev_t(fset->fset_dentry->d_inode->i_dev); + kdev_t dev = to_kdev_t(fset->fset_dentry->d_inode->i_sb->s_dev); if (errorval && errorval == (long)value && !is_read_only(dev)) { CDEBUG(D_SUPER, "setting device %s read only\n", kdevname(dev)); @@ -763,7 +763,7 @@ int presto_do_link(struct presto_file_set *fset, struct dentry *old_dentry, goto exit_lock; error = -EXDEV; - if (dir->d_inode->i_dev != inode->i_dev) + if (dir->d_inode->i_sb->s_dev != inode->i_sb->s_dev) goto exit_lock; /* @@ -1820,7 +1820,7 @@ int presto_rename_dir(struct presto_file_set *fset, struct dentry *old_parent, if (error) return error; - if (new_dir->i_dev != old_dir->i_dev) + if (new_dir->i_sb->s_dev != old_dir->i_sb->s_dev) return -EXDEV; if (!new_dentry->d_inode) @@ -1901,7 +1901,7 @@ int presto_rename_other(struct presto_file_set *fset, struct dentry *old_parent, if (error) return error; - if (new_dir->i_dev != old_dir->i_dev) + if (new_dir->i_sb->s_dev != old_dir->i_sb->s_dev) return -EXDEV; if (!new_dentry->d_inode) diff --git a/fs/locks.c b/fs/locks.c index d2b55e36a7da..525722e45a52 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1766,7 +1766,7 @@ static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx) #else /* kdevname is a broken interface. but we expose it to userspace */ out += sprintf(out, "%d %s:%ld ", fl->fl_pid, - inode ? kdevname(to_kdev_t(inode->i_dev)) : "", + inode ? kdevname(to_kdev_t(inode->i_sb->s_dev)) : "", inode ? inode->i_ino : 0); #endif if (IS_POSIX(fl)) { diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index dc7866c5d3ed..7a9500315bd3 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -226,7 +226,7 @@ encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) && (fhp->fh_export->ex_flags & NFSEXP_FSID)) p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid); else - p = xdr_encode_hyper(p, (u64) inode->i_dev); + p = xdr_encode_hyper(p, (u64) inode->i_sb->s_dev); p = xdr_encode_hyper(p, (u64) inode->i_ino); time.tv_sec = fhp->fh_post_atime; time.tv_nsec = 0; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 34486025974b..820e8de68827 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -632,7 +632,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, #endif /* Get readahead parameters */ - ra = nfsd_get_raparms(inode->i_dev, inode->i_ino); + ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino); if (ra) file.f_ra = ra->p_ra; @@ -752,7 +752,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, */ if (EX_WGATHER(exp)) { if (atomic_read(&inode->i_writecount) > 1 - || (last_ino == inode->i_ino && last_dev == inode->i_dev)) { + || (last_ino == inode->i_ino && last_dev == inode->i_sb->s_dev)) { dprintk("nfsd: write defer %d\n", current->pid); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((HZ+99)/100); @@ -769,7 +769,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, #endif } last_ino = inode->i_ino; - last_dev = inode->i_dev; + last_dev = inode->i_sb->s_dev; } dprintk("nfsd: write complete err=%d\n", err); diff --git a/fs/proc/array.c b/fs/proc/array.c index 862f02e13bec..450dd174d3ea 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -484,8 +484,9 @@ static int proc_pid_maps_get_line (char *buf, struct vm_area_struct *map) dev = 0; ino = 0; if (map->vm_file != NULL) { - dev = map->vm_file->f_dentry->d_inode->i_dev; - ino = map->vm_file->f_dentry->d_inode->i_ino; + struct inode *inode = map->vm_file->f_dentry->d_inode; + dev = inode->i_sb->s_dev; + ino = inode->i_ino; line = d_path(map->vm_file->f_dentry, map->vm_file->f_vfsmnt, buf, PAGE_SIZE); diff --git a/fs/stat.c b/fs/stat.c index 1b88da05b0d2..87e5e53e10a6 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -18,7 +18,7 @@ void generic_fillattr(struct inode *inode, struct kstat *stat) { - stat->dev = inode->i_dev; + stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; stat->mode = inode->i_mode; stat->nlink = inode->i_nlink; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index d1047e8aa65c..d0d4ee152347 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -918,7 +918,6 @@ __udf_read_inode(struct inode *inode) * Set defaults, but the inode is still incomplete! * Note: get_new_inode() sets the following on a new inode: * i_sb = sb - * i_dev = sb->s_dev; * i_no = ino * i_flags = sb->s_flags * i_state = 0 diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 3a9ffbc49f60..ff4cc2582bc2 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -247,8 +247,8 @@ finish_inode: /* * Read the disk inode attributes into a new inode structure and get * a new vnode for it. Initialize the inode lock so we can idestroy - * it soon if it's a dup. This should also initialize i_dev, i_ino, - * i_bno, i_mount, and i_index. + * it soon if it's a dup. This should also initialize i_ino, i_bno, + * i_mount, and i_index. */ error = xfs_iread(mp, tp, ino, &ip, bno); if (error) { diff --git a/fs/xfs/xfsidbg.c b/fs/xfs/xfsidbg.c index 3c2932453bf0..368940b38e5d 100644 --- a/fs/xfs/xfsidbg.c +++ b/fs/xfs/xfsidbg.c @@ -1639,7 +1639,7 @@ static void printinode(struct inode *ip) kdb_printf(" i_ino = %lu i_count = %u i_dev = 0x%x i_size %Ld\n", ip->i_ino, atomic_read(&ip->i_count), - ip->i_dev, ip->i_size); + ip->i_sb->s_dev, ip->i_size); kdb_printf( " i_mode = 0x%x i_nlink = %d i_rdev = 0x%x i_state = 0x%lx\n", diff --git a/include/linux/fs.h b/include/linux/fs.h index 7234a60f85a5..86c370cc3355 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -362,7 +362,6 @@ struct inode { struct list_head i_dentry; unsigned long i_ino; atomic_t i_count; - dev_t i_dev; umode_t i_mode; unsigned int i_nlink; uid_t i_uid; diff --git a/net/socket.c b/net/socket.c index 448ed2c395a3..e8b80586132a 100644 --- a/net/socket.c +++ b/net/socket.c @@ -468,7 +468,6 @@ struct socket *sock_alloc(void) if (!inode) return NULL; - inode->i_dev = 0; sock = SOCKET_I(inode); inode->i_mode = S_IFSOCK|S_IRWXUGO; -- cgit v1.2.3 From a359ceb1dbf8670b5ce320801f9f1eb31baacef7 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Thu, 21 Nov 2002 06:04:26 -0800 Subject: [PATCH] add necessary #ifdefs to netfilter_bridge.h, vs 2.5.48 --- include/linux/netfilter_bridge.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index b9063ac5a2c5..0f07bda8a7cf 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -6,7 +6,9 @@ #include #include +#if defined(__KERNEL__) && defined(CONFIG_NETFILTER) #include +#endif /* Bridge Hooks */ /* After promisc drops, checksum checks. */ @@ -23,6 +25,8 @@ #define NF_BR_BROUTING 5 #define NF_BR_NUMHOOKS 6 +#ifdef __KERNEL__ + #define BRNF_PKT_TYPE 0x01 #define BRNF_BRIDGED_DNAT 0x02 #define BRNF_DONT_TAKE_PARENT 0x04 @@ -38,6 +42,7 @@ enum nf_br_hook_priorities { NF_BR_PRI_LAST = INT_MAX, }; +#ifdef CONFIG_NETFILTER static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb) { @@ -57,5 +62,7 @@ struct bridge_skb_cb { __u32 ipv4; } daddr; }; +#endif /* CONFIG_NETFILTER */ +#endif /* __KERNEL__ */ #endif -- cgit v1.2.3 From eaac83e4de3624edcd9cc5458fb3f84e5526b8ba Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Thu, 21 Nov 2002 19:03:32 -0800 Subject: [PATCH] sonypi driver update The most important changes are: * add suspend/resume support to the sonypi driver (not based on driverfs however) (Florian Lohoff); * add "Zoom" and "Thumbphrase" buttons (Francois Gurin); * add camera and lid events for C1XE (Kunihiko IMAI); * add a mask parameter letting the user choose what kind of events he wants; * use ACPI ec_read/ec_write when available in order to play nice when latest ACPI is enabled; * several source cleanups. --- Documentation/sonypi.txt | 38 +++++-- drivers/char/sonypi.c | 288 ++++++++++++++++++++++++----------------------- drivers/char/sonypi.h | 143 ++++++++++++++++++----- include/linux/sonypi.h | 8 +- 4 files changed, 298 insertions(+), 179 deletions(-) (limited to 'include/linux') diff --git a/Documentation/sonypi.txt b/Documentation/sonypi.txt index 561a2e6de244..e887506fe85c 100644 --- a/Documentation/sonypi.txt +++ b/Documentation/sonypi.txt @@ -1,6 +1,7 @@ Sony Programmable I/O Control Device Driver Readme -------------------------------------------------- - Copyright (C) 2001 Stelian Pop , Alcôve + Copyright (C) 2001-2002 Stelian Pop + Copyright (C) 2001-2002 Alcôve Copyright (C) 2001 Michael Ashley Copyright (C) 2001 Junichi Morita Copyright (C) 2000 Takaya Kinjo @@ -15,14 +16,14 @@ generate, like: - capture button events (only on Vaio Picturebook series) - Fn keys - bluetooth button (only on C1VR model) - - back button (PCG-GR7/K model) - - lid open/close events (Z600NE model) + - programmable keys, back, help, zoom, thumbphrase buttons, etc. + (when available) Those events (see linux/sonypi.h) can be polled using the character device node /dev/sonypi (major 10, minor auto allocated or specified as a option). A simple daemon which translates the jogdial movements into mouse wheel events -can be downloaded at: +can be downloaded at: This driver supports also some ioctl commands for setting the LCD screen brightness and querying the batteries charge information (some more @@ -43,7 +44,7 @@ Several options can be passed to the sonypi driver, either by adding them to /etc/modules.conf file, when the driver is compiled as a module or by adding the following to the kernel command line (in your bootloader): - sonypi=minor[,verbose[,fnkeyinit[,camera[,compat[,nojogdial]]]]] + sonypi=minor[,verbose[,fnkeyinit[,camera[,compat[,mask]]]]] where: @@ -64,15 +65,36 @@ where: with it and it shouldn't be required anyway if ACPI is already enabled). - verbose: print unknown events from the sonypi device + verbose: set to 1 to print unknown events received from the + sonypi device. + set to 2 to print all events received from the + sonypi device. compat: uses some compatibility code for enabling the sonypi events. If the driver worked for you in the past (prior to version 1.5) and does not work anymore, add this option and report to the author. - nojogdial: gives more accurate PKEY events on those Vaio models - which don't have a jogdial (like the FX series). + mask: event mask telling the driver what events will be + reported to the user. This parameter is required for some + Vaio models where the hardware reuses values used in + other Vaio models (like the FX series who does not + have a jogdial but reuses the jogdial events for + programmable keys events). The default event mask is + set to 0xffffffff, meaning that all possible events will be + tried. You can use the following bits to construct + your own event mask (from drivers/char/sonypi.h): + SONYPI_JOGGER_MASK 0x0001 + SONYPI_CAPTURE_MASK 0x0002 + SONYPI_FNKEY_MASK 0x0004 + SONYPI_BLUETOOTH_MASK 0x0008 + SONYPI_PKEY_MASK 0x0010 + SONYPI_BACK_MASK 0x0020 + SONYPI_HELP_MASK 0x0040 + SONYPI_LID_MASK 0x0080 + SONYPI_ZOOM_MASK 0x0100 + SONYPI_THUMBPHRASE_MASK 0x0200 + SONYPI_MEYE_MASK 0x0400 Module use: ----------- diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index d76c86c12b68..76c5a42e2e19 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1,7 +1,9 @@ /* * Sony Programmable I/O Control Device driver for VAIO * - * Copyright (C) 2001 Stelian Pop , Alcôve + * Copyright (C) 2001-2002 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve * * Copyright (C) 2001 Michael Ashley * @@ -39,6 +41,7 @@ #include #include #include +#include #include #include @@ -53,7 +56,7 @@ static int verbose; /* = 0 */ static int fnkeyinit; /* = 0 */ static int camera; /* = 0 */ static int compat; /* = 0 */ -static int nojogdial; /* = 0 */ +static unsigned long mask = 0xffffffff; /* Inits the queue */ static inline void sonypi_initq(void) { @@ -113,29 +116,14 @@ static inline int sonypi_emptyq(void) { return result; } -static void sonypi_ecrset(u8 addr, u8 value) { - - wait_on_command(1, inb_p(SONYPI_CST_IOPORT) & 3); - outb_p(0x81, SONYPI_CST_IOPORT); - wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2); - outb_p(addr, SONYPI_DATA_IOPORT); - wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2); - outb_p(value, SONYPI_DATA_IOPORT); - wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2); -} - -static u8 sonypi_ecrget(u8 addr) { - - wait_on_command(1, inb_p(SONYPI_CST_IOPORT) & 3); - outb_p(0x80, SONYPI_CST_IOPORT); - wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2); - outb_p(addr, SONYPI_DATA_IOPORT); - wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2); - return inb_p(SONYPI_DATA_IOPORT); -} - -static u16 sonypi_ecrget16(u8 addr) { - return sonypi_ecrget(addr) | (sonypi_ecrget(addr + 1) << 8); +static int ec_read16(u8 addr, u16 *value) { + u8 val_lb, val_hb; + if (ec_read(addr, &val_lb)) + return -1; + if (ec_read(addr + 1, &val_hb)) + return -1; + *value = val_lb | (val_hb << 8); + return 0; } /* Initializes the device - this comes from the AML code in the ACPI bios */ @@ -162,9 +150,12 @@ static void __devinit sonypi_type1_srs(void) { } static void __devinit sonypi_type2_srs(void) { - sonypi_ecrset(SONYPI_SHIB, (sonypi_device.ioport1 & 0xFF00) >> 8); - sonypi_ecrset(SONYPI_SLOB, sonypi_device.ioport1 & 0x00FF); - sonypi_ecrset(SONYPI_SIRQ, sonypi_device.bits); + if (ec_write(SONYPI_SHIB, (sonypi_device.ioport1 & 0xFF00) >> 8)) + printk(KERN_WARNING "ec_write failed\n"); + if (ec_write(SONYPI_SLOB, sonypi_device.ioport1 & 0x00FF)) + printk(KERN_WARNING "ec_write failed\n"); + if (ec_write(SONYPI_SIRQ, sonypi_device.bits)) + printk(KERN_WARNING "ec_write failed\n"); udelay(10); } @@ -182,15 +173,18 @@ static void __devexit sonypi_type1_dis(void) { } static void __devexit sonypi_type2_dis(void) { - sonypi_ecrset(SONYPI_SHIB, 0); - sonypi_ecrset(SONYPI_SLOB, 0); - sonypi_ecrset(SONYPI_SIRQ, 0); + if (ec_write(SONYPI_SHIB, 0)) + printk(KERN_WARNING "ec_write failed\n"); + if (ec_write(SONYPI_SLOB, 0)) + printk(KERN_WARNING "ec_write failed\n"); + if (ec_write(SONYPI_SIRQ, 0)) + printk(KERN_WARNING "ec_write failed\n"); } static u8 sonypi_call1(u8 dev) { u8 v1, v2; - wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2, ITERATIONS_LONG); outb(dev, sonypi_device.ioport2); v1 = inb_p(sonypi_device.ioport2); v2 = inb_p(sonypi_device.ioport1); @@ -200,9 +194,9 @@ static u8 sonypi_call1(u8 dev) { static u8 sonypi_call2(u8 dev, u8 fn) { u8 v1; - wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2, ITERATIONS_LONG); outb(dev, sonypi_device.ioport2); - wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2, ITERATIONS_LONG); outb(fn, sonypi_device.ioport1); v1 = inb_p(sonypi_device.ioport1); return v1; @@ -211,11 +205,11 @@ static u8 sonypi_call2(u8 dev, u8 fn) { static u8 sonypi_call3(u8 dev, u8 fn, u8 v) { u8 v1; - wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2, ITERATIONS_LONG); outb(dev, sonypi_device.ioport2); - wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2, ITERATIONS_LONG); outb(fn, sonypi_device.ioport1); - wait_on_command(0, inb_p(sonypi_device.ioport2) & 2); + wait_on_command(0, inb_p(sonypi_device.ioport2) & 2, ITERATIONS_LONG); outb(v, sonypi_device.ioport1); v1 = inb_p(sonypi_device.ioport1); return v1; @@ -237,7 +231,7 @@ static u8 sonypi_read(u8 fn) { /* Set brightness, hue etc */ static void sonypi_set(u8 fn, u8 v) { - wait_on_command(0, sonypi_call3(0x90, fn, v)); + wait_on_command(0, sonypi_call3(0x90, fn, v), ITERATIONS_SHORT); } /* Tests if the camera is ready */ @@ -311,79 +305,30 @@ static void sonypi_setbluetoothpower(u8 state) { /* Interrupt handler: some event is available */ void sonypi_irq(int irq, void *dev_id, struct pt_regs *regs) { u8 v1, v2, event = 0; - int i; - u8 sonypi_jogger_ev, sonypi_fnkey_ev; - u8 sonypi_capture_ev, sonypi_bluetooth_ev; - u8 sonypi_pkey_ev; - - if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) { - sonypi_jogger_ev = SONYPI_TYPE2_JOGGER_EV; - sonypi_fnkey_ev = SONYPI_TYPE2_FNKEY_EV; - sonypi_capture_ev = SONYPI_TYPE2_CAPTURE_EV; - sonypi_bluetooth_ev = SONYPI_TYPE2_BLUETOOTH_EV; - sonypi_pkey_ev = nojogdial ? SONYPI_TYPE2_PKEY_EV - : SONYPI_TYPE1_PKEY_EV; - } - else { - sonypi_jogger_ev = SONYPI_TYPE1_JOGGER_EV; - sonypi_fnkey_ev = SONYPI_TYPE1_FNKEY_EV; - sonypi_capture_ev = SONYPI_TYPE1_CAPTURE_EV; - sonypi_bluetooth_ev = SONYPI_TYPE1_BLUETOOTH_EV; - sonypi_pkey_ev = SONYPI_TYPE1_PKEY_EV; - } + int i, j; v1 = inb_p(sonypi_device.ioport1); v2 = inb_p(sonypi_device.ioport2); - if ((v2 & sonypi_pkey_ev) == sonypi_pkey_ev) { - for (i = 0; sonypi_pkeyev[i].event; i++) - if (sonypi_pkeyev[i].data == v1) { - event = sonypi_pkeyev[i].event; - goto found; - } - } - if ((v2 & sonypi_jogger_ev) == sonypi_jogger_ev) { - for (i = 0; sonypi_joggerev[i].event; i++) - if (sonypi_joggerev[i].data == v1) { - event = sonypi_joggerev[i].event; - goto found; - } - } - if ((v2 & sonypi_capture_ev) == sonypi_capture_ev) { - for (i = 0; sonypi_captureev[i].event; i++) - if (sonypi_captureev[i].data == v1) { - event = sonypi_captureev[i].event; - goto found; - } - } - if ((v2 & sonypi_fnkey_ev) == sonypi_fnkey_ev) { - for (i = 0; sonypi_fnkeyev[i].event; i++) - if (sonypi_fnkeyev[i].data == v1) { - event = sonypi_fnkeyev[i].event; - goto found; - } - } - if ((v2 & sonypi_bluetooth_ev) == sonypi_bluetooth_ev) { - for (i = 0; sonypi_blueev[i].event; i++) - if (sonypi_blueev[i].data == v1) { - event = sonypi_blueev[i].event; - goto found; - } - } - if ((v2 & SONYPI_BACK_EV) == SONYPI_BACK_EV) { - for (i = 0; sonypi_backev[i].event; i++) - if (sonypi_backev[i].data == v1) { - event = sonypi_backev[i].event; - goto found; - } - } - if ((v2 & SONYPI_LID_EV) == SONYPI_LID_EV) { - for (i = 0; sonypi_lidev[i].event; i++) - if (sonypi_lidev[i].data == v1) { - event = sonypi_lidev[i].event; + if (verbose > 1) + printk(KERN_INFO + "sonypi: event port1=0x%02x,port2=0x%02x\n", v1, v2); + + for (i = 0; sonypi_eventtypes[i].model; i++) { + if (sonypi_device.model != sonypi_eventtypes[i].model) + continue; + if ((v2 & sonypi_eventtypes[i].data) != sonypi_eventtypes[i].data) + continue; + if (! (mask & sonypi_eventtypes[i].mask)) + continue; + for (j = 0; sonypi_eventtypes[i].events[j].event; j++) { + if (v1 == sonypi_eventtypes[i].events[j].data) { + event = sonypi_eventtypes[i].events[j].event; goto found; } + } } + if (verbose) printk(KERN_WARNING "sonypi: unknown event port1=0x%02x,port2=0x%02x\n",v1,v2); @@ -545,72 +490,77 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp, down(&sonypi_device.lock); switch (cmd) { case SONYPI_IOCGBRT: - val8 = sonypi_ecrget(SONYPI_LCD_LIGHT); - if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) { - ret = -EFAULT; - goto out; + if (ec_read(SONYPI_LCD_LIGHT, &val8)) { + ret = -EIO; + break; } + if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) + ret = -EFAULT; break; case SONYPI_IOCSBRT: if (copy_from_user(&val8, (u8 *)arg, sizeof(val8))) { ret = -EFAULT; - goto out; + break; } - sonypi_ecrset(SONYPI_LCD_LIGHT, val8); + if (ec_write(SONYPI_LCD_LIGHT, val8)) + ret = -EIO; break; case SONYPI_IOCGBAT1CAP: - val16 = sonypi_ecrget16(SONYPI_BAT1_FULL); - if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) { - ret = -EFAULT; - goto out; + if (ec_read16(SONYPI_BAT1_FULL, &val16)) { + ret = -EIO; + break; } + if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) + ret = -EFAULT; break; case SONYPI_IOCGBAT1REM: - val16 = sonypi_ecrget16(SONYPI_BAT1_LEFT); - if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) { - ret = -EFAULT; - goto out; + if (ec_read16(SONYPI_BAT1_FULL, &val16)) { + ret = -EIO; + break; } + if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) + ret = -EFAULT; break; case SONYPI_IOCGBAT2CAP: - val16 = sonypi_ecrget16(SONYPI_BAT2_FULL); - if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) { - ret = -EFAULT; - goto out; + if (ec_read16(SONYPI_BAT1_FULL, &val16)) { + ret = -EIO; + break; } + if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) + ret = -EFAULT; break; case SONYPI_IOCGBAT2REM: - val16 = sonypi_ecrget16(SONYPI_BAT2_LEFT); - if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) { - ret = -EFAULT; - goto out; + if (ec_read16(SONYPI_BAT1_FULL, &val16)) { + ret = -EIO; + break; } + if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) + ret = -EFAULT; break; case SONYPI_IOCGBATFLAGS: - val8 = sonypi_ecrget(SONYPI_BAT_FLAGS) & 0x07; - if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) { - ret = -EFAULT; - goto out; + if (ec_read(SONYPI_BAT_FLAGS, &val8)) { + ret = -EIO; + break; } + val8 &= 0x07; + if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) + ret = -EFAULT; break; case SONYPI_IOCGBLUE: val8 = sonypi_device.bluetooth_power; - if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) { + if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) ret = -EFAULT; - goto out; - } break; case SONYPI_IOCSBLUE: if (copy_from_user(&val8, (u8 *)arg, sizeof(val8))) { ret = -EFAULT; - goto out; + break; } sonypi_setbluetoothpower(val8); break; default: ret = -EINVAL; } -out: up(&sonypi_device.lock); return ret; } @@ -621,7 +571,7 @@ static struct file_operations sonypi_misc_fops = { .poll = sonypi_misc_poll, .open = sonypi_misc_open, .release = sonypi_misc_release, - .fasync = sonypi_misc_fasync, + .fasync = sonypi_misc_fasync, .ioctl = sonypi_misc_ioctl, }; @@ -629,6 +579,51 @@ struct miscdevice sonypi_misc_device = { -1, "sonypi", &sonypi_misc_fops }; +#if CONFIG_PM +static int sonypi_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) { + static int old_camera_power; + + switch (rqst) { + case PM_SUSPEND: + sonypi_call2(0x81, 0); /* make sure we don't get any more events */ + if (camera) { + old_camera_power = sonypi_device.camera_power; + sonypi_camera_off(); + } + if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) + sonypi_type2_dis(); + else + sonypi_type1_dis(); +#if !defined(CONFIG_ACPI) + /* disable ACPI mode */ + if (fnkeyinit) + outb(0xf1, 0xb2); +#endif + break; + case PM_RESUME: +#if !defined(CONFIG_ACPI) + /* Enable ACPI mode to get Fn key events */ + if (fnkeyinit) + outb(0xf0, 0xb2); +#endif + if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) + sonypi_type2_srs(); + else + sonypi_type1_srs(); + sonypi_call1(0x82); + sonypi_call2(0x81, 0xff); + if (compat) + sonypi_call1(0x92); + else + sonypi_call1(0x82); + if (camera && old_camera_power) + sonypi_camera_on(); + break; + } + return 0; +} +#endif + static int __devinit sonypi_probe(struct pci_dev *pcidev) { int i, ret; struct sonypi_ioport_list *ioport_list; @@ -720,14 +715,14 @@ static int __devinit sonypi_probe(struct pci_dev *pcidev) { SONYPI_DRIVER_MINORVERSION); printk(KERN_INFO "sonypi: detected %s model, " "verbose = %s, fnkeyinit = %s, camera = %s, " - "compat = %s, nojogdial = %s\n", + "compat = %s, mask = 0x%08lx\n", (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE1) ? "type1" : "type2", verbose ? "on" : "off", fnkeyinit ? "on" : "off", camera ? "on" : "off", compat ? "on" : "off", - nojogdial ? "on" : "off"); + mask); printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n", sonypi_device.irq, sonypi_device.ioport1, sonypi_device.ioport2); @@ -735,6 +730,10 @@ static int __devinit sonypi_probe(struct pci_dev *pcidev) { printk(KERN_INFO "sonypi: device allocated minor is %d\n", sonypi_misc_device.minor); +#if CONFIG_PM + sonypi_device.pm = pm_register(PM_PCI_DEV, 0, sonypi_pm_callback); +#endif + return 0; out3: @@ -746,6 +745,11 @@ out1: } static void __devexit sonypi_remove(void) { + +#if CONFIG_PM + pm_unregister(sonypi_device.pm); +#endif + sonypi_call2(0x81, 0); /* make sure we don't get any more events */ if (camera) sonypi_camera_off(); @@ -803,7 +807,7 @@ static int __init sonypi_setup(char *str) { compat = ints[5]; if (ints[0] == 5) goto out; - nojogdial = ints[6]; + mask = ints[6]; out: return 1; } @@ -815,7 +819,7 @@ __setup("sonypi=", sonypi_setup); module_init(sonypi_init_module); module_exit(sonypi_cleanup_module); -MODULE_AUTHOR("Stelian Pop "); +MODULE_AUTHOR("Stelian Pop "); MODULE_DESCRIPTION("Sony Programmable I/O Control Device driver"); MODULE_LICENSE("GPL"); @@ -830,7 +834,7 @@ MODULE_PARM(camera,"i"); MODULE_PARM_DESC(camera, "set this if you have a MotionEye camera (PictureBook series)"); MODULE_PARM(compat,"i"); MODULE_PARM_DESC(compat, "set this if you want to enable backward compatibility mode"); -MODULE_PARM(nojogdial, "i"); -MODULE_PARM_DESC(nojogdial, "set this if you have a Vaio without a jogdial (like the fx series)"); +MODULE_PARM(mask, "i"); +MODULE_PARM_DESC(mask, "set this to the mask of event you want to enable (see doc)"); EXPORT_SYMBOL(sonypi_camera_command); diff --git a/drivers/char/sonypi.h b/drivers/char/sonypi.h index 25acec82eb0f..5b219ee74b6d 100644 --- a/drivers/char/sonypi.h +++ b/drivers/char/sonypi.h @@ -1,7 +1,9 @@ /* * Sony Programmable I/O Control Device driver for VAIO * - * Copyright (C) 2001 Stelian Pop , Alcôve + * Copyright (C) 2001-2002 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve * * Copyright (C) 2001 Michael Ashley * @@ -35,10 +37,16 @@ #ifdef __KERNEL__ #define SONYPI_DRIVER_MAJORVERSION 1 -#define SONYPI_DRIVER_MINORVERSION 14 +#define SONYPI_DRIVER_MINORVERSION 15 + +#define SONYPI_DEVICE_MODEL_TYPE1 1 +#define SONYPI_DEVICE_MODEL_TYPE2 2 +#include #include #include +#include +#include #include "linux/sonypi.h" /* type1 models use those */ @@ -145,25 +153,23 @@ static struct sonypi_irq_list sonypi_type2_irq_list[] = { #define SONYPI_CAMERA_REVISION 8 #define SONYPI_CAMERA_ROMVERSION 9 -/* key press event data (ioport2) */ -#define SONYPI_TYPE1_JOGGER_EV 0x10 -#define SONYPI_TYPE2_JOGGER_EV 0x08 -#define SONYPI_TYPE1_CAPTURE_EV 0x60 -#define SONYPI_TYPE2_CAPTURE_EV 0x08 -#define SONYPI_TYPE1_FNKEY_EV 0x20 -#define SONYPI_TYPE2_FNKEY_EV 0x08 -#define SONYPI_TYPE1_BLUETOOTH_EV 0x30 -#define SONYPI_TYPE2_BLUETOOTH_EV 0x08 -#define SONYPI_TYPE1_PKEY_EV 0x40 -#define SONYPI_TYPE2_PKEY_EV 0x08 -#define SONYPI_BACK_EV 0x08 -#define SONYPI_LID_EV 0x38 +/* Event masks */ +#define SONYPI_JOGGER_MASK 0x00000001 +#define SONYPI_CAPTURE_MASK 0x00000002 +#define SONYPI_FNKEY_MASK 0x00000004 +#define SONYPI_BLUETOOTH_MASK 0x00000008 +#define SONYPI_PKEY_MASK 0x00000010 +#define SONYPI_BACK_MASK 0x00000020 +#define SONYPI_HELP_MASK 0x00000040 +#define SONYPI_LID_MASK 0x00000080 +#define SONYPI_ZOOM_MASK 0x00000100 +#define SONYPI_THUMBPHRASE_MASK 0x00000200 +#define SONYPI_MEYE_MASK 0x00000400 struct sonypi_event { u8 data; u8 event; }; - /* The set of possible jogger events */ static struct sonypi_event sonypi_joggerev[] = { { 0x1f, SONYPI_EVENT_JOGDIAL_UP }, @@ -180,7 +186,7 @@ static struct sonypi_event sonypi_joggerev[] = { { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED }, { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED }, { 0x00, SONYPI_EVENT_JOGDIAL_RELEASED }, - { 0x00, 0x00 } + { 0, 0 } }; /* The set of possible capture button events */ @@ -189,7 +195,7 @@ static struct sonypi_event sonypi_captureev[] = { { 0x07, SONYPI_EVENT_CAPTURE_PRESSED }, { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED }, { 0x00, SONYPI_EVENT_CAPTURE_RELEASED }, - { 0x00, 0x00 } + { 0, 0 } }; /* The set of possible fnkeys events */ @@ -215,7 +221,7 @@ static struct sonypi_event sonypi_fnkeyev[] = { { 0x34, SONYPI_EVENT_FNKEY_S }, { 0x35, SONYPI_EVENT_FNKEY_B }, { 0x36, SONYPI_EVENT_FNKEY_ONLY }, - { 0x00, 0x00 } + { 0, 0 } }; /* The set of possible program key events */ @@ -223,7 +229,7 @@ static struct sonypi_event sonypi_pkeyev[] = { { 0x01, SONYPI_EVENT_PKEY_P1 }, { 0x02, SONYPI_EVENT_PKEY_P2 }, { 0x04, SONYPI_EVENT_PKEY_P3 }, - { 0x00, 0x00 } + { 0, 0 } }; /* The set of possible bluetooth events */ @@ -231,21 +237,74 @@ static struct sonypi_event sonypi_blueev[] = { { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED }, { 0x59, SONYPI_EVENT_BLUETOOTH_ON }, { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF }, - { 0x00, 0x00 } + { 0, 0 } }; /* The set of possible back button events */ static struct sonypi_event sonypi_backev[] = { { 0x20, SONYPI_EVENT_BACK_PRESSED }, + { 0, 0 } +}; + +/* The set of possible help button events */ +static struct sonypi_event sonypi_helpev[] = { { 0x3b, SONYPI_EVENT_HELP_PRESSED }, - { 0x00, 0x00 } + { 0, 0 } }; + /* The set of possible lid events */ static struct sonypi_event sonypi_lidev[] = { { 0x51, SONYPI_EVENT_LID_CLOSED }, { 0x50, SONYPI_EVENT_LID_OPENED }, - { 0x00, 0x00 } + { 0, 0 } +}; + +/* The set of possible zoom events */ +static struct sonypi_event sonypi_zoomev[] = { + { 0x3a, SONYPI_EVENT_ZOOM_PRESSED }, + { 0, 0 } +}; + +/* The set of possible thumbphrase events */ +static struct sonypi_event sonypi_thumbphraseev[] = { + { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED }, + { 0, 0 } +}; + +/* The set of possible motioneye camera events */ +static struct sonypi_event sonypi_meyeev[] = { + { 0x00, SONYPI_EVENT_MEYE_FACE }, + { 0x01, SONYPI_EVENT_MEYE_OPPOSITE }, + { 0, 0 } +}; + +struct sonypi_eventtypes { + int model; + u8 data; + unsigned long mask; + struct sonypi_event * events; +} sonypi_eventtypes[] = { + { SONYPI_DEVICE_MODEL_TYPE1, 0x70, SONYPI_MEYE_MASK, sonypi_meyeev }, + { SONYPI_DEVICE_MODEL_TYPE1, 0x30, SONYPI_LID_MASK, sonypi_lidev }, + { SONYPI_DEVICE_MODEL_TYPE1, 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { SONYPI_DEVICE_MODEL_TYPE1, 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev }, + { SONYPI_DEVICE_MODEL_TYPE1, 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { SONYPI_DEVICE_MODEL_TYPE1, 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, + { SONYPI_DEVICE_MODEL_TYPE1, 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev }, + + { SONYPI_DEVICE_MODEL_TYPE2, 0x38, SONYPI_LID_MASK, sonypi_lidev }, + { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_JOGGER_MASK, sonypi_joggerev }, + { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, + { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_BACK_MASK, sonypi_backev }, + { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_HELP_MASK, sonypi_helpev }, + { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_ZOOM_MASK, sonypi_zoomev }, + { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev }, + + { 0, 0, 0, 0 } }; #define SONYPI_BUF_SIZE 128 @@ -259,9 +318,6 @@ struct sonypi_queue { unsigned char buf[SONYPI_BUF_SIZE]; }; -#define SONYPI_DEVICE_MODEL_TYPE1 1 -#define SONYPI_DEVICE_MODEL_TYPE2 2 - struct sonypi_device { struct pci_dev *dev; u16 irq; @@ -275,16 +331,47 @@ struct sonypi_device { struct sonypi_queue queue; int open_count; int model; +#if CONFIG_PM + struct pm_dev *pm; +#endif }; -#define wait_on_command(quiet, command) { \ - unsigned int n = 10000; \ +#define ITERATIONS_LONG 10000 +#define ITERATIONS_SHORT 10 + +#define wait_on_command(quiet, command, iterations) { \ + unsigned int n = iterations; \ while (--n && (command)) \ udelay(1); \ if (!n && (verbose || !quiet)) \ printk(KERN_WARNING "sonypi command failed at %s : %s (line %d)\n", __FILE__, __FUNCTION__, __LINE__); \ } +#if !defined(CONFIG_ACPI) +extern int verbose; + +static inline int ec_write(u8 addr, u8 value) { + wait_on_command(1, inb_p(SONYPI_CST_IOPORT) & 3, ITERATIONS_LONG); + outb_p(0x81, SONYPI_CST_IOPORT); + wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2, ITERATIONS_LONG); + outb_p(addr, SONYPI_DATA_IOPORT); + wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2, ITERATIONS_LONG); + outb_p(value, SONYPI_DATA_IOPORT); + wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2, ITERATIONS_LONG); + return 0; +} + +static inline int ec_read(u8 addr, u8 *value) { + wait_on_command(1, inb_p(SONYPI_CST_IOPORT) & 3, ITERATIONS_LONG); + outb_p(0x80, SONYPI_CST_IOPORT); + wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2, ITERATIONS_LONG); + outb_p(addr, SONYPI_DATA_IOPORT); + wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2, ITERATIONS_LONG); + *value = inb_p(SONYPI_DATA_IOPORT); + return 0; +} +#endif /* !CONFIG_ACPI */ + #endif /* __KERNEL__ */ #endif /* _SONYPI_PRIV_H_ */ diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index 8828b98b3029..7581d3fd846d 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h @@ -1,7 +1,9 @@ /* * Sony Programmable I/O Control Device driver for VAIO * - * Copyright (C) 2001 Stelian Pop , Alcôve + * Copyright (C) 2001-2002 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve * * Copyright (C) 2001 Michael Ashley * @@ -85,6 +87,10 @@ #define SONYPI_EVENT_JOGDIAL_VFAST_UP 47 #define SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED 48 #define SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED 49 +#define SONYPI_EVENT_ZOOM_PRESSED 50 +#define SONYPI_EVENT_THUMBPHRASE_PRESSED 51 +#define SONYPI_EVENT_MEYE_FACE 52 +#define SONYPI_EVENT_MEYE_OPPOSITE 53 /* get/set brightness */ #define SONYPI_IOCGBRT _IOR('v', 0, __u8) -- cgit v1.2.3 From 034f2e115e00f9c9f1315452d639366ad114f902 Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Thu, 21 Nov 2002 19:03:44 -0800 Subject: [PATCH] meye driver update The most important changes are: - allocate buffers on open(), not module load; - correct some failed allocation paths; - use wait_event; - C99 structs inits; --- Documentation/video4linux/meye.txt | 5 +- drivers/media/video/meye.c | 153 +++++++++++++++++-------------------- drivers/media/video/meye.h | 6 +- include/linux/meye.h | 4 +- 4 files changed, 81 insertions(+), 87 deletions(-) (limited to 'include/linux') diff --git a/Documentation/video4linux/meye.txt b/Documentation/video4linux/meye.txt index 6b22fcb7aa51..722eef4ef9f1 100644 --- a/Documentation/video4linux/meye.txt +++ b/Documentation/video4linux/meye.txt @@ -1,6 +1,7 @@ Vaio Picturebook Motion Eye Camera Driver Readme ------------------------------------------------ - Copyright (C) 2001 Stelian Pop , Alcôve + Copyright (C) 2001-2002 Stelian Pop + Copyright (C) 2001-2002 Alcôve Copyright (C) 2000 Andrew Tridgell This driver enable the use of video4linux compatible applications with the @@ -52,7 +53,7 @@ Usage: or xawtv -c /dev/video0 -geometry 320x240 - motioneye () + motioneye () for getting ppm or jpg snapshots, mjpeg video Private API: diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 8ddf1dfb0eb3..16d949c96153 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1,7 +1,9 @@ /* * Motion Eye video4linux driver for Sony Vaio PictureBook * - * Copyright (C) 2001 Stelian Pop , Alcôve + * Copyright (C) 2001-2002 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve * * Copyright (C) 2000 Andrew Tridgell * @@ -169,16 +171,28 @@ static int ptable_alloc(void) { meye.mchip_ptable[MCHIP_NB_PAGES] = pci_alloc_consistent(meye.mchip_dev, PAGE_SIZE, &meye.mchip_dmahandle); - if (!meye.mchip_ptable[MCHIP_NB_PAGES]) + if (!meye.mchip_ptable[MCHIP_NB_PAGES]) { + meye.mchip_dmahandle = 0; return -1; + } pt = (u32 *)meye.mchip_ptable[MCHIP_NB_PAGES]; for (i = 0; i < MCHIP_NB_PAGES; i++) { meye.mchip_ptable[i] = pci_alloc_consistent(meye.mchip_dev, PAGE_SIZE, pt); - if (!meye.mchip_ptable[i]) + if (!meye.mchip_ptable[i]) { + int j; + pt = (u32 *)meye.mchip_ptable[MCHIP_NB_PAGES]; + for (j = 0; j < i; ++j) { + pci_free_consistent(meye.mchip_dev, + PAGE_SIZE, + meye.mchip_ptable[j], *pt); + pt++; + } + meye.mchip_dmahandle = 0; return -1; + } pt++; } return 0; @@ -189,11 +203,13 @@ static void ptable_free(void) { int i; pt = (u32 *)meye.mchip_ptable[MCHIP_NB_PAGES]; - for (i = 0; i < MCHIP_NB_PAGES; i++) + for (i = 0; i < MCHIP_NB_PAGES; i++) { if (meye.mchip_ptable[i]) pci_free_consistent(meye.mchip_dev, PAGE_SIZE, meye.mchip_ptable[i], *pt); + pt++; + } if (meye.mchip_ptable[MCHIP_NB_PAGES]) pci_free_consistent(meye.mchip_dev, @@ -569,6 +585,16 @@ static void mchip_vrj_setup(u8 mode) { mchip_load_tables(); } +/* sets the DMA parameters into the chip */ +static void mchip_dma_setup(u32 dma_addr) { + int i; + + mchip_set(MCHIP_MM_PT_ADDR, dma_addr); + for (i = 0; i < 4; i++) + mchip_set(MCHIP_MM_FIR(i), 0); + meye.mchip_fnum = 0; +} + /* setup for DMA transfers - also zeros the framebuffer */ static int mchip_dma_alloc(void) { if (!meye.mchip_dmahandle) @@ -579,18 +605,10 @@ static int mchip_dma_alloc(void) { /* frees the DMA buffer */ static void mchip_dma_free(void) { - if (meye.mchip_dmahandle) + if (meye.mchip_dmahandle) { + mchip_dma_setup(0); ptable_free(); -} - -/* sets the DMA parameters into the chip */ -static void mchip_dma_setup(void) { - int i; - - mchip_set(MCHIP_MM_PT_ADDR, meye.mchip_dmahandle); - for (i = 0; i < 4; i++) - mchip_set(MCHIP_MM_FIR(i), 0); - meye.mchip_fnum = 0; + } } /* stop any existing HIC action and wait for any dma to complete then @@ -698,7 +716,7 @@ static void mchip_take_picture(void) { mchip_hic_stop(); mchip_subsample(); - mchip_dma_setup(); + mchip_dma_setup(meye.mchip_dmahandle); mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP); mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); @@ -741,7 +759,7 @@ static void mchip_continuous_start(void) { mchip_hic_stop(); mchip_subsample(); mchip_set_framerate(); - mchip_dma_setup(); + mchip_dma_setup(meye.mchip_dmahandle); meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; @@ -801,7 +819,7 @@ static void mchip_cont_compression_start(void) { mchip_vrj_setup(0x3f); mchip_subsample(); mchip_set_framerate(); - mchip_dma_setup(); + mchip_dma_setup(meye.mchip_dmahandle); meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; @@ -823,7 +841,7 @@ static void meye_irq(int irq, void *dev_id, struct pt_regs *regs) { while (1) { v = mchip_get_frame(); if (!(v & MCHIP_MM_FIR_RDY)) - goto out; + return; switch (meye.mchip_mode) { case MCHIP_HIC_MODE_CONT_OUT: @@ -856,12 +874,11 @@ static void meye_irq(int irq, void *dev_id, struct pt_regs *regs) { default: /* do not free frame, since it can be a snap */ - goto out; + return; } /* switch */ mchip_free_frame(); } -out: } /****************************************************************************/ @@ -889,6 +906,7 @@ static int meye_open(struct inode *inode, struct file *file) { static int meye_release(struct inode *inode, struct file *file) { mchip_hic_stop(); + mchip_dma_free(); video_exclusive_release(inode,file); return 0; } @@ -957,7 +975,6 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, case VIDIOCSYNC: { int *i = arg; - DECLARE_WAITQUEUE(wait, current); if (*i < 0 || *i >= gbuffers) return -EINVAL; @@ -967,18 +984,9 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, case MEYE_BUF_UNUSED: return -EINVAL; case MEYE_BUF_USING: - add_wait_queue(&meye.grabq.proc_list, &wait); - current->state = TASK_INTERRUPTIBLE; - while (meye.grab_buffer[*i].state == MEYE_BUF_USING) { - schedule(); - if(signal_pending(current)) { - remove_wait_queue(&meye.grabq.proc_list, &wait); - current->state = TASK_RUNNING; - return -EINTR; - } - } - remove_wait_queue(&meye.grabq.proc_list, &wait); - current->state = TASK_RUNNING; + if (wait_event_interruptible(meye.grabq.proc_list, + (meye.grab_buffer[*i].state != MEYE_BUF_USING))) + return -EINTR; /* fall through */ case MEYE_BUF_DONE: meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; @@ -1095,7 +1103,6 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, case MEYEIOC_SYNC: { int *i = arg; - DECLARE_WAITQUEUE(wait, current); if (*i < 0 || *i >= gbuffers) return -EINVAL; @@ -1105,18 +1112,9 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, case MEYE_BUF_UNUSED: return -EINVAL; case MEYE_BUF_USING: - add_wait_queue(&meye.grabq.proc_list, &wait); - current->state = TASK_INTERRUPTIBLE; - while (meye.grab_buffer[*i].state == MEYE_BUF_USING) { - schedule(); - if(signal_pending(current)) { - remove_wait_queue(&meye.grabq.proc_list, &wait); - current->state = TASK_RUNNING; - return -EINTR; - } - } - remove_wait_queue(&meye.grabq.proc_list, &wait); - current->state = TASK_RUNNING; + if (wait_event_interruptible(meye.grabq.proc_list, + (meye.grab_buffer[*i].state != MEYE_BUF_USING))) + return -EINTR; /* fall through */ case MEYE_BUF_DONE: meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; @@ -1211,20 +1209,20 @@ static int meye_mmap(struct file *file, struct vm_area_struct *vma) { } static struct file_operations meye_fops = { - owner: THIS_MODULE, - open: meye_open, - release: meye_release, - mmap: meye_mmap, - ioctl: meye_ioctl, - llseek: no_llseek, + .owner = THIS_MODULE, + .open = meye_open, + .release = meye_release, + .mmap = meye_mmap, + .ioctl = meye_ioctl, + .llseek = no_llseek, }; static struct video_device meye_template = { - owner: THIS_MODULE, - name: "meye", - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_MEYE, - fops: &meye_fops, + .owner = THIS_MODULE, + .name = "meye", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_MEYE, + .fops = &meye_fops, }; static int __devinit meye_probe(struct pci_dev *pcidev, @@ -1244,15 +1242,9 @@ static int __devinit meye_probe(struct pci_dev *pcidev, meye.mchip_dev = pcidev; memcpy(&meye.video_dev, &meye_template, sizeof(meye_template)); - if (mchip_dma_alloc()) { - printk(KERN_ERR "meye: mchip framebuffer allocation failed\n"); - ret = -ENOMEM; - goto out2; - } - if ((ret = pci_enable_device(meye.mchip_dev))) { printk(KERN_ERR "meye: pci_enable_device failed\n"); - goto out3; + goto out2; } meye.mchip_irq = pcidev->irq; @@ -1260,14 +1252,14 @@ static int __devinit meye_probe(struct pci_dev *pcidev, if (!mchip_adr) { printk(KERN_ERR "meye: mchip has no device base address\n"); ret = -EIO; - goto out4; + goto out3; } if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0), pci_resource_len(meye.mchip_dev, 0), "meye")) { ret = -EIO; printk(KERN_ERR "meye: request_mem_region failed\n"); - goto out4; + goto out3; } pci_read_config_byte(meye.mchip_dev, PCI_REVISION_ID, &revision); @@ -1280,14 +1272,14 @@ static int __devinit meye_probe(struct pci_dev *pcidev, if ((ret = request_irq(meye.mchip_irq, meye_irq, SA_INTERRUPT | SA_SHIRQ, "meye", meye_irq))) { printk(KERN_ERR "meye: request_irq failed (ret=%d)\n", ret); - goto out5; + goto out4; } meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS); if (!meye.mchip_mmregs) { printk(KERN_ERR "meye: ioremap failed\n"); ret = -EIO; - goto out6; + goto out5; } /* Ask the camera to perform a soft reset. */ @@ -1309,7 +1301,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev, printk(KERN_ERR "meye: video_register_device failed\n"); ret = -EIO; - goto out7; + goto out6; } printk(KERN_INFO "meye: Motion Eye Camera Driver v%d.%d.\n", @@ -1343,17 +1335,15 @@ static int __devinit meye_probe(struct pci_dev *pcidev, sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48); return 0; -out7: - iounmap(meye.mchip_mmregs); out6: - free_irq(meye.mchip_irq, meye_irq); + iounmap(meye.mchip_mmregs); out5: + free_irq(meye.mchip_irq, meye_irq); +out4: release_mem_region(pci_resource_start(meye.mchip_dev, 0), pci_resource_len(meye.mchip_dev, 0)); -out4: - pci_disable_device(meye.mchip_dev); out3: - mchip_dma_free(); + pci_disable_device(meye.mchip_dev); out2: sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0); out1: @@ -1371,7 +1361,6 @@ static void __devexit meye_remove(struct pci_dev *pcidev) { free_irq(meye.mchip_irq, meye_irq); - iounmap(meye.mchip_mmregs); release_mem_region(pci_resource_start(meye.mchip_dev, 0), @@ -1398,10 +1387,10 @@ static struct pci_device_id meye_pci_tbl[] __devinitdata = { MODULE_DEVICE_TABLE(pci, meye_pci_tbl); static struct pci_driver meye_driver = { - name: "meye", - id_table: meye_pci_tbl, - probe: meye_probe, - remove: __devexit_p(meye_remove), + .name = "meye", + .id_table = meye_pci_tbl, + .probe = meye_probe, + .remove = __devexit_p(meye_remove), }; static int __init meye_init_module(void) { @@ -1441,7 +1430,7 @@ out: __setup("meye=", meye_setup); #endif -MODULE_AUTHOR("Stelian Pop "); +MODULE_AUTHOR("Stelian Pop "); MODULE_DESCRIPTION("video4linux driver for the MotionEye camera"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h index d5111e93db85..bdff18e10043 100644 --- a/drivers/media/video/meye.h +++ b/drivers/media/video/meye.h @@ -1,7 +1,9 @@ /* * Motion Eye video4linux driver for Sony Vaio PictureBook * - * Copyright (C) 2001 Stelian Pop , Alcôve + * Copyright (C) 2001-2002 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve * * Copyright (C) 2000 Andrew Tridgell * @@ -29,7 +31,7 @@ #define _MEYE_PRIV_H_ #define MEYE_DRIVER_MAJORVERSION 1 -#define MEYE_DRIVER_MINORVERSION 4 +#define MEYE_DRIVER_MINORVERSION 5 /****************************************************************************/ /* Motion JPEG chip registers */ diff --git a/include/linux/meye.h b/include/linux/meye.h index b6146960823a..367cfeb69528 100644 --- a/include/linux/meye.h +++ b/include/linux/meye.h @@ -1,7 +1,9 @@ /* * Motion Eye video4linux driver for Sony Vaio PictureBook * - * Copyright (C) 2001 Stelian Pop , Alcôve + * Copyright (C) 2001-2002 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve * * Copyright (C) 2000 Andrew Tridgell * -- cgit v1.2.3 From 645e448cd42b611ad1ed24a38efff6c667a4a658 Mon Sep 17 00:00:00 2001 From: Ivan Kokshaysky Date: Thu, 21 Nov 2002 19:04:52 -0800 Subject: [PATCH] PCI: rename exported pbus_* functions Traditional naming in pci/setup-xx code assumes that pdev_*/pbus_* functions are private, everything visible from outer world should be pci_*. --- drivers/hotplug/cpci_hotplug_pci.c | 4 ++-- drivers/pci/setup-bus.c | 16 ++++++++-------- include/linux/pci.h | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/hotplug/cpci_hotplug_pci.c b/drivers/hotplug/cpci_hotplug_pci.c index ae161439e08b..7a06eca3a807 100644 --- a/drivers/hotplug/cpci_hotplug_pci.c +++ b/drivers/hotplug/cpci_hotplug_pci.c @@ -411,8 +411,8 @@ static int cpci_configure_bridge(struct pci_bus* bus, struct pci_dev* dev) * Update the bridge resources of the bridge to accommodate devices * behind it. */ - pbus_size_bridges(child); - pbus_assign_resources(child); + pci_bus_size_bridges(child); + pci_bus_assign_resources(child); /* Enable resource mapping via command register */ command = PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR; diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 52248c5e2bf5..5be491ff1a35 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -333,13 +333,13 @@ pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type) } void __devinit -pbus_size_bridges(struct pci_bus *bus) +pci_bus_size_bridges(struct pci_bus *bus) { struct list_head *ln; unsigned long mask, type; for (ln=bus->children.next; ln != &bus->children; ln=ln->next) - pbus_size_bridges(pci_bus_b(ln)); + pci_bus_size_bridges(pci_bus_b(ln)); /* The root bus? */ if (!bus->self) @@ -358,10 +358,10 @@ pbus_size_bridges(struct pci_bus *bus) } pbus_size_mem(bus, mask, type); } -EXPORT_SYMBOL(pbus_size_bridges); +EXPORT_SYMBOL(pci_bus_size_bridges); void __devinit -pbus_assign_resources(struct pci_bus *bus) +pci_bus_assign_resources(struct pci_bus *bus) { struct list_head *ln; int found_vga = pbus_assign_resources_sorted(bus); @@ -377,11 +377,11 @@ pbus_assign_resources(struct pci_bus *bus) for (ln=bus->children.next; ln != &bus->children; ln=ln->next) { struct pci_bus *b = pci_bus_b(ln); - pbus_assign_resources(b); + pci_bus_assign_resources(b); pci_setup_bridge(b); } } -EXPORT_SYMBOL(pbus_assign_resources); +EXPORT_SYMBOL(pci_bus_assign_resources); void __init pci_assign_unassigned_resources(void) @@ -392,10 +392,10 @@ pci_assign_unassigned_resources(void) /* Depth first, calculate sizes and alignments of all subordinate buses. */ for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) - pbus_size_bridges(pci_bus_b(ln)); + pci_bus_size_bridges(pci_bus_b(ln)); /* Depth last, allocate resources and update the hardware. */ for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) - pbus_assign_resources(pci_bus_b(ln)); + pci_bus_assign_resources(pci_bus_b(ln)); pci_for_each_dev(dev) { pdev_enable_device(dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index 3fa04d5f93f8..30c4a9dbc855 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -601,8 +601,8 @@ int pci_enable_wake(struct pci_dev *dev, u32 state, int enable); /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ -void pbus_assign_resources(struct pci_bus *bus); -void pbus_size_bridges(struct pci_bus *bus); +void pci_bus_assign_resources(struct pci_bus *bus); +void pci_bus_size_bridges(struct pci_bus *bus); int pci_claim_resource(struct pci_dev *, int); void pci_assign_unassigned_resources(void); void pdev_enable_device(struct pci_dev *); -- cgit v1.2.3 From 0c7eef6a3c19cb2d31dc4c1d468e5f429aff0aed Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Thu, 21 Nov 2002 19:06:16 -0800 Subject: [PATCH] cpufreq: cleanups This changes the return type of the verify and setpolicy functions from void to int. While doing this, I've changed the values for minimum and maximum supported frequency to be per CPU, as UltraSPARC needs this. Additionally, small cleanups in various drivers. --- Documentation/cpufreq | 31 +++++---- arch/arm/mach-integrator/cpu.c | 11 ++- arch/arm/mach-sa1100/cpu-sa1100.c | 8 ++- arch/arm/mach-sa1100/cpu-sa1110.c | 12 ++-- arch/arm/mach-sa1100/generic.c | 3 +- arch/i386/kernel/cpu/cpufreq/elanfreq.c | 30 ++++---- arch/i386/kernel/cpu/cpufreq/longhaul.c | 34 +++++---- arch/i386/kernel/cpu/cpufreq/longrun.c | 24 ++++--- arch/i386/kernel/cpu/cpufreq/p4-clockmod.c | 28 ++++---- arch/i386/kernel/cpu/cpufreq/powernow-k6.c | 30 ++++---- arch/i386/kernel/cpu/cpufreq/speedstep.c | 25 +++---- drivers/acpi/processor.c | 30 ++++---- include/linux/cpufreq.h | 22 +++--- kernel/cpufreq.c | 106 +++++++++++++++++++---------- 14 files changed, 226 insertions(+), 168 deletions(-) (limited to 'include/linux') diff --git a/Documentation/cpufreq b/Documentation/cpufreq index 3f5c8229cf1a..902c8558873a 100644 --- a/Documentation/cpufreq +++ b/Documentation/cpufreq @@ -87,7 +87,8 @@ PowerNow! K6: Transmeta Crusoe Longrun: Transmeta Crusoe processors: -------------------------------- - Does not work with the 2.4. /proc/sys/cpu/ interface. + It is recommended to use the 2.6. /proc/cpufreq interface when + using this driver @@ -283,15 +284,17 @@ entries: cpufreq_verify_t verify: This is a pointer to a function with the following definition: - void verify_function (struct cpufreq_policy *policy). + int verify_function (struct cpufreq_policy *policy). This function must verify the new policy is within the limits supported by the CPU, and at least one supported CPU is within this range. It may be useful to use cpufreq.h / - cpufreq_verify_within_limits for this. + cpufreq_verify_within_limits for this. If this is called with + CPUFREQ_ALL_CPUS, and there is no common subset of frequencies + for all CPUs, exit with an error. cpufreq_setpolicy_t setpolicy: This is a pointer to a function with the following definition: - void setpolicy_function (struct cpufreq_policy *policy). + int setpolicy_function (struct cpufreq_policy *policy). This function must set the CPU to the new policy. If it is a "dumb" CPU which only allows fixed frequencies to be set, it shall set it to the lowest within the limit for @@ -302,30 +305,30 @@ cpufreq_setpolicy_t setpolicy: This is a pointer to a function with struct cpufreq_policy *policy: This is an array of NR_CPUS struct cpufreq_policies, containing the current policies set for these - CPUs. Note that policy[0].max_cpu_freq must contain the - absolute maximum CPU frequency supported by _all_ CPUs. + CPUs. Note that policy[cpu].max_cpu_freq must contain the + absolute maximum CPU frequency supported by the specified cpu. In case the driver is expected to run with the 2.4.-style API (/proc/sys/cpu/.../), two more values must be passed #ifdef CONFIG_CPU_FREQ_24_API - unsigned int cpu_min_freq; + unsigned int cpu_min_freq[NR_CPUS]; unsigned int cpu_cur_freq[NR_CPUS]; #endif - with cpu_min_freq being the minimum CPU frequency supported by - the CPUs; and the entries in cpu_cur_freq reflecting the - current speed of the appropriate CPU. + with cpu_min_freq[cpu] being the minimum CPU frequency + supported by the CPU; and the entries in cpu_cur_freq + reflecting the current speed of the appropriate CPU. Some Requirements to CPUFreq architecture drivers ------------------------------------------------- * Only call cpufreq_register() when the ability to switch CPU - frequencies is _verified_ or can't be missing + frequencies is _verified_ or can't be missing. Also, all + other initialization must be done beofre this call, as + cpfureq_register calls the driver's verify and setpolicy code for + each CPU. * cpufreq_unregister() may only be called if cpufreq_register() has been successfully(!) called before. * kfree() the struct cpufreq_driver only after the call to cpufreq_unregister(), unless cpufreq_register() failed. -* Be aware that there is currently no error management in the - setpolicy() code in the CPUFreq core. So only call yourself a - cpufreq_driver if you are really a working cpufreq_driver! diff --git a/arch/arm/mach-integrator/cpu.c b/arch/arm/mach-integrator/cpu.c index 773955c92d14..23a8788ef76e 100644 --- a/arch/arm/mach-integrator/cpu.c +++ b/arch/arm/mach-integrator/cpu.c @@ -73,7 +73,7 @@ static struct vco freq_to_vco(unsigned int freq_khz, int factor) * Validate the speed in khz. If it is outside our * range, then return the lowest. */ -static void integrator_verify_speed(struct cpufreq_policy *policy) +static int integrator_verify_speed(struct cpufreq_policy *policy) { struct vco vco; @@ -93,6 +93,8 @@ static void integrator_verify_speed(struct cpufreq_policy *policy) vco.vdw = 152; policy->min = policy->max = vco_to_freq(vco, 1); + + return 0; } static void do_set_policy(int cpu, struct cpufreq_policy *policy) @@ -116,7 +118,7 @@ static void do_set_policy(int cpu, struct cpufreq_policy *policy) __raw_writel(0, CM_LOCK); } -static void integrator_set_policy(struct cpufreq_policy *policy) +static int integrator_set_policy(struct cpufreq_policy *policy) { unsigned long cpus_allowed; int cpu; @@ -139,6 +141,8 @@ static void integrator_set_policy(struct cpufreq_policy *policy) * Restore the CPUs allowed mask. */ set_cpus_allowed(current, cpus_allowed); + + return 0; } static struct cpufreq_policy integrator_policy = { @@ -151,7 +155,6 @@ static struct cpufreq_driver integrator_driver = { .verify = integrator_verify_speed, .setpolicy = integrator_set_policy, .policy = &integrator_policy, - .cpu_min_freq = 12000, }; #endif @@ -202,6 +205,8 @@ static int __init integrator_cpu_init(void) set_cpus_allowed(current, cpus_allowed); #ifdef CONFIG_CPU_FREQ + for (cpu=0; cpumax); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; } static struct cpufreq_policy sa1100_policy = { @@ -208,7 +210,7 @@ static struct cpufreq_driver sa1100_driver = { .verify = sa11x0_verify_speed, .setpolicy = sa1100_setspeed, .policy = &sa1100_policy, - .cpu_min_freq = 59000, + .cpu_min_freq[0]= 59000, }; static int __init sa1100_dram_init(void) @@ -216,7 +218,7 @@ static int __init sa1100_dram_init(void) int ret = -ENODEV; if ((processor_id & CPU_SA1100_MASK) == CPU_SA1100_ID) { - sa1100_driver.cpu_curr_freq[0] = + sa1100_driver.cpu_cur_freq[0] = sa1100_policy.min = sa1100_policy.max = sa11x0_getspeed(); diff --git a/arch/arm/mach-sa1100/cpu-sa1110.c b/arch/arm/mach-sa1100/cpu-sa1110.c index ee0b097fc2a8..69223d1daa2f 100644 --- a/arch/arm/mach-sa1100/cpu-sa1110.c +++ b/arch/arm/mach-sa1100/cpu-sa1110.c @@ -212,7 +212,7 @@ sdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram) * above, we can match for an exact frequency. If we don't find * an exact match, we will to set the lowest frequency to be safe. */ -static void sa1110_setspeed(struct cpufreq_policy *policy) +static int sa1110_setspeed(struct cpufreq_policy *policy) { struct sdram_params *sdram = &sdram_params; struct cpufreq_freqs freqs; @@ -291,6 +291,8 @@ static void sa1110_setspeed(struct cpufreq_policy *policy) sdram_update_refresh(policy->max, sdram); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; } static struct cpufreq_policy sa1110_policy = { @@ -300,10 +302,10 @@ static struct cpufreq_policy sa1110_policy = { }; static struct cpufreq_driver sa1110_driver = { - .verify = sa11x0_verify_speed, - .setpolicy = sa1110_setspeed, - .policy = &sa1110_policy, - .cpu_min_freq = 59000, + .verify = sa11x0_verify_speed, + .setpolicy = sa1110_setspeed, + .policy = &sa1110_policy, + .cpu_min_freq[0] = 59000, }; static int __init sa1110_clk_init(void) diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index 461bec6b956b..8b7f34ad6153 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -67,7 +67,7 @@ unsigned int sa11x0_freq_to_ppcr(unsigned int khz) * scaling, so we force min=max, and set the policy to "performance". * If we can't generate the precise frequency requested, round it up. */ -void sa11x0_verify_speed(struct cpufreq_policy *policy) +int sa11x0_verify_speed(struct cpufreq_policy *policy) { if (policy->max > policy->max_cpu_freq) policy->max = policy->max_cpu_freq; @@ -75,6 +75,7 @@ void sa11x0_verify_speed(struct cpufreq_policy *policy) policy->max = cclk_frequency_100khz[sa11x0_freq_to_ppcr(policy->max)] * 100; policy->min = policy->max; policy->policy = CPUFREQ_POLICY_POWERSAVE; + return 0; } unsigned int sa11x0_getspeed(void) diff --git a/arch/i386/kernel/cpu/cpufreq/elanfreq.c b/arch/i386/kernel/cpu/cpufreq/elanfreq.c index d5485109c61e..bc71cc376350 100644 --- a/arch/i386/kernel/cpu/cpufreq/elanfreq.c +++ b/arch/i386/kernel/cpu/cpufreq/elanfreq.c @@ -172,13 +172,13 @@ static void elanfreq_set_cpu_state (unsigned int state) { * for the hardware supported by the driver. */ -static void elanfreq_verify (struct cpufreq_policy *policy) +static int elanfreq_verify (struct cpufreq_policy *policy) { unsigned int number_states = 0; unsigned int i; if (!policy || !max_freq) - return; + return -EINVAL; policy->cpu = 0; @@ -190,7 +190,7 @@ static void elanfreq_verify (struct cpufreq_policy *policy) number_states++; if (number_states) - return; + return 0; for (i=(sizeof(elan_multiplier)/sizeof(struct s_elan_multiplier) - 1); i>=0; i--) if (elan_multiplier[i].clock < policy->max) @@ -198,16 +198,16 @@ static void elanfreq_verify (struct cpufreq_policy *policy) policy->max = elan_multiplier[i+1].clock; - return; + return 0; } -static void elanfreq_setpolicy (struct cpufreq_policy *policy) +static int elanfreq_setpolicy (struct cpufreq_policy *policy) { unsigned int number_states = 0; unsigned int i, j=4; if (!elanfreq_driver) - return; + return -EINVAL; for (i=(sizeof(elan_multiplier)/sizeof(struct s_elan_multiplier) - 1); i>=0; i--) if ((elan_multiplier[i].clock >= policy->min) && @@ -219,7 +219,7 @@ static void elanfreq_setpolicy (struct cpufreq_policy *policy) if (number_states == 1) { elanfreq_set_cpu_state(j); - return; + return 0; } switch (policy->policy) { @@ -236,14 +236,14 @@ static void elanfreq_setpolicy (struct cpufreq_policy *policy) j = i; break; default: - return; + return -EINVAL; } if (elan_multiplier[j].clock > max_freq) - BUG(); + return -EINVAL; elanfreq_set_cpu_state(j); - return; + return 0; } @@ -296,7 +296,7 @@ static int __init elanfreq_init(void) max_freq = elanfreq_get_cpu_frequency(); #ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_min_freq = 1000; + driver->cpu_min_freq[0] = 1000; driver->cpu_cur_freq[0] = elanfreq_get_cpu_frequency(); #endif @@ -309,15 +309,15 @@ static int __init elanfreq_init(void) driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; driver->policy[0].max_cpu_freq = max_freq; + elanfreq_driver = driver; + ret = cpufreq_register(driver); if (ret) { + elanfreq_driver = NULL; kfree(driver); - return ret; } - elanfreq_driver = driver; - - return 0; + return ret; } diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c index aa684efaa1cb..511d52051512 100644 --- a/arch/i386/kernel/cpu/cpufreq/longhaul.c +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c @@ -1,5 +1,5 @@ /* - * $Id: longhaul.c,v 1.72 2002/09/29 23:43:10 db Exp $ + * $Id: longhaul.c,v 1.77 2002/10/31 21:17:40 db Exp $ * * (C) 2001 Dave Jones. * (C) 2002 Padraig Brady. @@ -436,8 +436,10 @@ static void __init longhaul_get_ranges (void) switch (longhaul) { case 1: /* Ugh, Longhaul v1 didn't have the min/max MSRs. - Assume max = whatever we booted at. */ + Assume min=3.0x & max = whatever we booted at. */ + minmult = 30; maxmult = longhaul_get_cpu_mult(); + minfsb = maxfsb = current_fsb; break; case 2 ... 3: @@ -531,7 +533,7 @@ static inline unsigned int longhaul_statecount_fsb(struct cpufreq_policy *policy } -static void longhaul_verify(struct cpufreq_policy *policy) +static int longhaul_verify(struct cpufreq_policy *policy) { unsigned int number_states = 0; unsigned int i; @@ -540,7 +542,7 @@ static void longhaul_verify(struct cpufreq_policy *policy) unsigned int newmax = -1; if (!policy || !longhaul_driver) - return; + return -EINVAL; policy->cpu = 0; cpufreq_verify_within_limits(policy, lowest_speed, highest_speed); @@ -552,7 +554,7 @@ static void longhaul_verify(struct cpufreq_policy *policy) number_states = longhaul_statecount_fsb(policy, current_fsb); if (number_states) - return; + return 0; /* get frequency closest above current policy->max */ if (can_scale_fsb==1) { @@ -579,10 +581,12 @@ static void longhaul_verify(struct cpufreq_policy *policy) } policy->max = newmax; + + return 0; } -static void longhaul_setpolicy (struct cpufreq_policy *policy) +static int longhaul_setpolicy (struct cpufreq_policy *policy) { unsigned int number_states = 0; unsigned int i; @@ -592,7 +596,7 @@ static void longhaul_setpolicy (struct cpufreq_policy *policy) unsigned int best_freq = -1; if (!longhaul_driver) - return; + return -EINVAL; if (policy->policy==CPUFREQ_POLICY_PERFORMANCE) fsb_search_table = perf_fsb_table; @@ -613,7 +617,7 @@ static void longhaul_setpolicy (struct cpufreq_policy *policy) } if (!number_states) - return; + return -EINVAL; else if (number_states == 1) { for(i=0; ipolicy = (struct cpufreq_policy *) (driver + 1); #ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_min_freq = (unsigned int) lowest_speed; + driver->cpu_min_freq[0] = (unsigned int) lowest_speed; driver->cpu_cur_freq[0] = currentspeed; #endif @@ -788,15 +792,15 @@ static int __init longhaul_init (void) driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; driver->policy[0].max_cpu_freq = (unsigned int) highest_speed; - ret = cpufreq_register(driver); + longhaul_driver = driver; + ret = cpufreq_register(driver); if (ret) { + longhaul_driver = NULL; kfree(driver); - return ret; } - longhaul_driver = driver; - return 0; + return ret; } diff --git a/arch/i386/kernel/cpu/cpufreq/longrun.c b/arch/i386/kernel/cpu/cpufreq/longrun.c index ab8cb3548450..982314536c79 100644 --- a/arch/i386/kernel/cpu/cpufreq/longrun.c +++ b/arch/i386/kernel/cpu/cpufreq/longrun.c @@ -1,5 +1,5 @@ /* - * $Id: longrun.c,v 1.12 2002/09/29 23:43:10 db Exp $ + * $Id: longrun.c,v 1.14 2002/10/31 21:17:40 db Exp $ * * (C) 2002 Dominik Brodowski * @@ -67,13 +67,13 @@ static void longrun_get_policy(struct cpufreq_policy *policy) * Sets a new CPUFreq policy on LongRun-capable processors. This function * has to be called with cpufreq_driver locked. */ -static void longrun_set_policy(struct cpufreq_policy *policy) +static int longrun_set_policy(struct cpufreq_policy *policy) { u32 msr_lo, msr_hi; u32 pctg_lo, pctg_hi; if (!longrun_driver || !policy) - return; + return -EINVAL; pctg_lo = (policy->min - longrun_low_freq) / ((longrun_high_freq - longrun_low_freq) / 100); @@ -105,7 +105,7 @@ static void longrun_set_policy(struct cpufreq_policy *policy) msr_hi |= pctg_hi; wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi); - return; + return 0; } @@ -115,16 +115,16 @@ static void longrun_set_policy(struct cpufreq_policy *policy) * Validates a new CPUFreq policy. This function has to be called with * cpufreq_driver locked. */ -static void longrun_verify_policy(struct cpufreq_policy *policy) +static int longrun_verify_policy(struct cpufreq_policy *policy) { if (!policy || !longrun_driver) - return; + return -EINVAL; policy->cpu = 0; cpufreq_verify_within_limits(policy, 0, longrun_driver->policy[0].max_cpu_freq); - return; + return 0; } @@ -252,20 +252,22 @@ static int __init longrun_init(void) longrun_get_policy(&driver->policy[0]); #ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_min_freq = longrun_low_freq; + driver->cpu_min_freq[0] = longrun_low_freq; driver->cpu_cur_freq[0] = longrun_high_freq; /* dummy value */ #endif driver->verify = &longrun_verify_policy; driver->setpolicy = &longrun_set_policy; + + longrun_driver = driver; + result = cpufreq_register(driver); if (result) { + longrun_driver = NULL; kfree(driver); - return result; } - longrun_driver = driver; - return 0; + return result; } diff --git a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c index e9c846879fe4..a86f3cc32f7a 100644 --- a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c +++ b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c @@ -78,7 +78,7 @@ static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) } #endif set_cpus_allowed(current, affected_cpu_map); - BUG_ON(!(smp_processor_id() & affected_cpu_map)); + BUG_ON(!(affected_cpu_map & (1 << smp_processor_id()))); /* get current state */ rdmsr(MSR_IA32_THERM_CONTROL, l, h); @@ -136,14 +136,14 @@ static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) } -static void cpufreq_p4_setpolicy(struct cpufreq_policy *policy) +static int cpufreq_p4_setpolicy(struct cpufreq_policy *policy) { unsigned int i; unsigned int newstate = 0; unsigned int number_states = 0; if (!cpufreq_p4_driver || !stock_freq || !policy) - return; + return -EINVAL; if (policy->policy == CPUFREQ_POLICY_POWERSAVE) { @@ -183,16 +183,17 @@ static void cpufreq_p4_setpolicy(struct cpufreq_policy *policy) min_state = newstate - (number_states - 1); } } */ + return 0; } -static void cpufreq_p4_verify(struct cpufreq_policy *policy) +static int cpufreq_p4_verify(struct cpufreq_policy *policy) { unsigned int number_states = 0; unsigned int i; if (!cpufreq_p4_driver || !stock_freq || !policy) - return; + return -EINVAL; if (!cpu_online(policy->cpu)) policy->cpu = CPUFREQ_ALL_CPUS; @@ -205,10 +206,10 @@ static void cpufreq_p4_verify(struct cpufreq_policy *policy) number_states++; if (number_states) - return; + return 0; policy->max = (stock_freq / 8) * (((unsigned int) ((policy->max * 8) / stock_freq)) + 1); - return; + return 0; } @@ -255,9 +256,10 @@ int __init cpufreq_p4_init(void) stock_freq = cpu_khz; #ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_min_freq = stock_freq / 8; - for (i=0;icpu_min_freq[i] = stock_freq / 8; driver->cpu_cur_freq[i] = stock_freq; + } #endif driver->verify = &cpufreq_p4_verify; @@ -274,15 +276,15 @@ int __init cpufreq_p4_init(void) driver->policy[i].cpu = i; } + cpufreq_p4_driver = driver; + ret = cpufreq_register(driver); if (ret) { + cpufreq_p4_driver = NULL; kfree(driver); - return ret; } - cpufreq_p4_driver = driver; - - return 0; + return ret; } diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k6.c b/arch/i386/kernel/cpu/cpufreq/powernow-k6.c index 2f77db051412..c43c9a6ddac2 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k6.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k6.c @@ -1,5 +1,5 @@ /* - * $Id: powernow-k6.c,v 1.33 2002/09/29 23:43:11 db Exp $ + * $Id: powernow-k6.c,v 1.36 2002/10/31 21:17:40 db Exp $ * This file was part of Powertweak Linux (http://powertweak.sf.net) * and is shared with the Linux Kernel module. * @@ -113,13 +113,13 @@ static void powernow_k6_set_state (unsigned int best_i) * Policy must be within lowest and highest possible CPU Frequency, * and at least one possible state must be within min and max. */ -static void powernow_k6_verify(struct cpufreq_policy *policy) +static int powernow_k6_verify(struct cpufreq_policy *policy) { unsigned int number_states = 0; unsigned int i, j; if (!policy || !busfreq) - return; + return -EINVAL; policy->cpu = 0; cpufreq_verify_within_limits(policy, (20 * busfreq), @@ -131,7 +131,7 @@ static void powernow_k6_verify(struct cpufreq_policy *policy) number_states++; if (number_states) - return; + return 0; /* no state is available within range -- find next larger state */ @@ -144,7 +144,7 @@ static void powernow_k6_verify(struct cpufreq_policy *policy) policy->max = clock_ratio[j] * busfreq; - return; + return 0; } @@ -154,13 +154,13 @@ static void powernow_k6_verify(struct cpufreq_policy *policy) * * sets a new CPUFreq policy */ -static void powernow_k6_setpolicy (struct cpufreq_policy *policy) +static int powernow_k6_setpolicy (struct cpufreq_policy *policy) { unsigned int number_states = 0; unsigned int i, j=4; if (!powernow_driver) - return; + return -EINVAL; for (i=0; i<8; i++) if ((policy->min <= (busfreq * clock_ratio[i])) && @@ -174,7 +174,7 @@ static void powernow_k6_setpolicy (struct cpufreq_policy *policy) /* if only one state is within the limit borders, it is easily detected and set */ powernow_k6_set_state(j); - return; + return 0; } /* more than one state within limit */ @@ -196,14 +196,14 @@ static void powernow_k6_setpolicy (struct cpufreq_policy *policy) j = i; break; default: - return; + return -EINVAL; } if (clock_ratio[i] > max_multiplier) - BUG(); + return -EINVAL; powernow_k6_set_state(j); - return; + return 0; } @@ -242,7 +242,7 @@ static int __init powernow_k6_init(void) driver->policy = (struct cpufreq_policy *) (driver + 1); #ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_min_freq = busfreq * 20; + driver->cpu_min_freq[0] = busfreq * 20; driver->cpu_cur_freq[0] = busfreq * max_multiplier; #endif @@ -255,16 +255,16 @@ static int __init powernow_k6_init(void) driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; driver->policy[0].max_cpu_freq = busfreq * max_multiplier; + powernow_driver = driver; result = cpufreq_register(driver); if (result) { release_region (POWERNOW_IOPORT, 16); + powernow_driver = NULL; kfree(driver); - return result; } - powernow_driver = driver; - return 0; + return result; } diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep.c b/arch/i386/kernel/cpu/cpufreq/speedstep.c index d5ee5fb11a69..2e7e50fca818 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep.c @@ -1,5 +1,5 @@ /* - * $Id: speedstep.c,v 1.57 2002/11/05 12:01:12 db Exp $ + * $Id: speedstep.c,v 1.58 2002/11/11 15:35:46 db Exp $ * * (C) 2001 Dave Jones, Arjan van de ven. * (C) 2002 Dominik Brodowski @@ -567,10 +567,10 @@ static int speedstep_detect_speeds (void) * * Sets a new CPUFreq policy. */ -static void speedstep_setpolicy (struct cpufreq_policy *policy) +static int speedstep_setpolicy (struct cpufreq_policy *policy) { if (!speedstep_driver || !policy) - return; + return -EINVAL; if (policy->min > speedstep_low_freq) speedstep_set_state(SPEEDSTEP_HIGH, 1); @@ -585,6 +585,7 @@ static void speedstep_setpolicy (struct cpufreq_policy *policy) speedstep_set_state(SPEEDSTEP_HIGH, 1); } } + return 0; } @@ -595,11 +596,11 @@ static void speedstep_setpolicy (struct cpufreq_policy *policy) * Limit must be within speedstep_low_freq and speedstep_high_freq, with * at least one border included. */ -static void speedstep_verify (struct cpufreq_policy *policy) +static int speedstep_verify (struct cpufreq_policy *policy) { if (!policy || !speedstep_driver || !speedstep_low_freq || !speedstep_high_freq) - return; + return -EINVAL; policy->cpu = 0; /* UP only */ @@ -609,7 +610,7 @@ static void speedstep_verify (struct cpufreq_policy *policy) (policy->max < speedstep_high_freq)) policy->max = speedstep_high_freq; - return; + return 0; } @@ -654,12 +655,11 @@ static int __init speedstep_init(void) speedstep_processor = speedstep_detect_processor(); if ((!speedstep_chipset) || (!speedstep_processor)) { - printk(KERN_INFO "a 0x%x b 0x%x\n", speedstep_processor, speedstep_chipset); printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this %s not (yet) available.\n", speedstep_chipset ? "processor" : "chipset"); return -ENODEV; } - dprintk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) support $Revision: 1.57 $\n"); + dprintk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) support $Revision: 1.58 $\n"); dprintk(KERN_DEBUG "cpufreq: chipset 0x%x - processor 0x%x\n", speedstep_chipset, speedstep_processor); @@ -693,7 +693,7 @@ static int __init speedstep_init(void) driver->policy = (struct cpufreq_policy *) (driver + 1); #ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_min_freq = speedstep_low_freq; + driver->cpu_min_freq[0] = speedstep_low_freq; driver->cpu_cur_freq[0] = speed; #endif @@ -707,14 +707,15 @@ static int __init speedstep_init(void) driver->policy[0].policy = (speed == speedstep_low_freq) ? CPUFREQ_POLICY_POWERSAVE : CPUFREQ_POLICY_PERFORMANCE; + speedstep_driver = driver; + result = cpufreq_register(driver); if (result) { + speedstep_driver = NULL; kfree(driver); - return result; } - speedstep_driver = driver; - return 0; + return result; } diff --git a/drivers/acpi/processor.c b/drivers/acpi/processor.c index 46b64579ef5e..13b8adb64ce2 100644 --- a/drivers/acpi/processor.c +++ b/drivers/acpi/processor.c @@ -1613,7 +1613,7 @@ acpi_processor_get_limit_info ( cpufreq interface -------------------------------------------------------------------------- */ #ifdef CONFIG_ACPI_PROCESSOR_PERF -static void +static int acpi_cpufreq_setpolicy ( struct cpufreq_policy *policy) { @@ -1626,7 +1626,7 @@ acpi_cpufreq_setpolicy ( ACPI_FUNCTION_TRACE("acpi_cpufreq_setpolicy"); if (!policy) - return_VOID; + return_VALUE(-EINVAL); /* get a present, initialized CPU */ if (policy->cpu == CPUFREQ_ALL_CPUS) @@ -1644,7 +1644,7 @@ acpi_cpufreq_setpolicy ( cpu = policy->cpu; pr = processors[cpu]; if (!pr) - return_VOID; + return_VALUE(-EINVAL); } /* select appropriate P-State */ @@ -1686,11 +1686,11 @@ acpi_cpufreq_setpolicy ( result = acpi_processor_set_performance (pr, next_state); } - return_VOID; + return_VALUE(0); } -static void +static int acpi_cpufreq_verify ( struct cpufreq_policy *policy) { @@ -1703,7 +1703,7 @@ acpi_cpufreq_verify ( ACPI_FUNCTION_TRACE("acpi_cpufreq_verify"); if (!policy) - return_VOID; + return_VALUE(-EINVAL); /* get a present, initialized CPU */ if (policy->cpu == CPUFREQ_ALL_CPUS) @@ -1721,7 +1721,7 @@ acpi_cpufreq_verify ( cpu = policy->cpu; pr = processors[cpu]; if (!pr) - return_VOID; + return_VALUE(-EINVAL); } /* first check if min and max are within valid limits */ @@ -1741,13 +1741,12 @@ acpi_cpufreq_verify ( next_larger_state = i; } - if (number_states) - return_VOID; - - /* round up now */ - policy->max = pr->performance.states[next_larger_state].core_frequency * 1000; + if (!number_states) { + /* round up now */ + policy->max = pr->performance.states[next_larger_state].core_frequency * 1000; + } - return_VOID; + return_VALUE(0); } static int @@ -1807,9 +1806,10 @@ acpi_cpufreq_init ( driver->policy = (struct cpufreq_policy *) (driver + 1); #ifdef CONFIG_CPU_FREQ_24_API - driver->cpu_min_freq = pr->performance.states[pr->performance.state_count - 1].core_frequency * 1000; - for (i=0;icpu_cur_freq[0] = pr->performance.states[current_state].core_frequency * 1000; + driver->cpu_min_freq[0] = pr->performance.states[pr->performance.state_count - 1].core_frequency * 1000; + } #endif driver->verify = &acpi_cpufreq_verify; diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index a5aa299cf92c..a8f8d2a31936 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -5,7 +5,7 @@ * (C) 2002 Dominik Brodowski * * - * $Id: cpufreq.h,v 1.27 2002/10/08 14:54:23 db Exp $ + * $Id: cpufreq.h,v 1.29 2002/11/11 15:35:47 db Exp $ * * 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 @@ -104,7 +104,7 @@ static inline unsigned long cpufreq_scale(unsigned long old, u_int div, u_int mu * CPUFREQ DRIVER INTERFACE * *********************************************************************/ -typedef void (*cpufreq_policy_t) (struct cpufreq_policy *policy); +typedef int (*cpufreq_policy_t) (struct cpufreq_policy *policy); struct cpufreq_driver { /* needed by all drivers */ @@ -116,7 +116,7 @@ struct cpufreq_driver { #endif /* 2.4. compatible API */ #ifdef CONFIG_CPU_FREQ_24_API - unsigned int cpu_min_freq; + unsigned int cpu_min_freq[NR_CPUS]; unsigned int cpu_cur_freq[NR_CPUS]; #endif }; @@ -205,19 +205,19 @@ enum { CPU_NR_FREQ = 3, }; -#define CTL_CPU_VARS_SPEED_MAX { \ +#define CTL_CPU_VARS_SPEED_MAX(cpunr) { \ .ctl_name = CPU_NR_FREQ_MAX, \ - .data = &cpu_max_freq, \ + .data = &cpu_max_freq[cpunr], \ .procname = "speed-max", \ - .maxlen = sizeof(cpu_max_freq),\ + .maxlen = sizeof(cpu_max_freq[cpunr]),\ .mode = 0444, \ .proc_handler = proc_dointvec, } -#define CTL_CPU_VARS_SPEED_MIN { \ +#define CTL_CPU_VARS_SPEED_MIN(cpunr) { \ .ctl_name = CPU_NR_FREQ_MIN, \ - .data = &cpu_min_freq, \ + .data = &cpu_min_freq[cpunr], \ .procname = "speed-min", \ - .maxlen = sizeof(cpu_min_freq),\ + .maxlen = sizeof(cpu_min_freq[cpunr]),\ .mode = 0444, \ .proc_handler = proc_dointvec, } @@ -230,8 +230,8 @@ enum { .extra1 = (void*) (cpunr), } #define CTL_TABLE_CPU_VARS(cpunr) static ctl_table ctl_cpu_vars_##cpunr[] = {\ - CTL_CPU_VARS_SPEED_MAX, \ - CTL_CPU_VARS_SPEED_MIN, \ + CTL_CPU_VARS_SPEED_MAX(cpunr), \ + CTL_CPU_VARS_SPEED_MIN(cpunr), \ CTL_CPU_VARS_SPEED(cpunr), \ { .ctl_name = 0, }, } diff --git a/kernel/cpufreq.c b/kernel/cpufreq.c index 74b54ac2cea7..1cfc2489a08a 100644 --- a/kernel/cpufreq.c +++ b/kernel/cpufreq.c @@ -4,7 +4,7 @@ * Copyright (C) 2001 Russell King * (C) 2002 Dominik Brodowski * - * $Id: cpufreq.c,v 1.45 2002/10/08 14:54:23 db Exp $ + * $Id: cpufreq.c,v 1.50 2002/11/11 15:35:48 db Exp $ * * 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 @@ -69,8 +69,8 @@ static struct cpufreq_policy default_policy = { /** * A few values needed by the 2.4.-compatible API */ -static unsigned int cpu_max_freq; -static unsigned int cpu_min_freq; +static unsigned int cpu_max_freq[NR_CPUS]; +static unsigned int cpu_min_freq[NR_CPUS]; static unsigned int cpu_cur_freq[NR_CPUS]; #endif @@ -228,6 +228,10 @@ static int cpufreq_proc_read ( continue; cpufreq_get_policy(&policy, i); + + if (!policy.max_cpu_freq) + continue; + min_pctg = (policy.min * 100) / policy.max_cpu_freq; max_pctg = (policy.max * 100) / policy.max_cpu_freq; @@ -378,7 +382,7 @@ int cpufreq_setmax(unsigned int cpu) { if (!cpu_online(cpu) && (cpu != CPUFREQ_ALL_CPUS)) return -EINVAL; - return cpufreq_set(cpu_max_freq, cpu); + return cpufreq_set(cpu_max_freq[cpu], cpu); } EXPORT_SYMBOL_GPL(cpufreq_setmax); @@ -807,13 +811,14 @@ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) policy->min = cpufreq_driver->policy[cpu].min; policy->max = cpufreq_driver->policy[cpu].max; policy->policy = cpufreq_driver->policy[cpu].policy; - policy->max_cpu_freq = cpufreq_driver->policy[0].max_cpu_freq; + policy->max_cpu_freq = cpufreq_driver->policy[cpu].max_cpu_freq; policy->cpu = cpu; up(&cpufreq_driver_sem); return 0; } +EXPORT_SYMBOL(cpufreq_get_policy); /** @@ -825,6 +830,7 @@ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) int cpufreq_set_policy(struct cpufreq_policy *policy) { unsigned int i; + int ret; down(&cpufreq_driver_sem); if (!cpufreq_driver || !cpufreq_driver->verify || @@ -834,12 +840,20 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) return -EINVAL; } - down(&cpufreq_notifier_sem); + if (policy->cpu == CPUFREQ_ALL_CPUS) + policy->max_cpu_freq = cpufreq_driver->policy[0].max_cpu_freq; + else + policy->max_cpu_freq = cpufreq_driver->policy[policy->cpu].max_cpu_freq; - policy->max_cpu_freq = cpufreq_driver->policy[0].max_cpu_freq; /* verify the cpu speed can be set within this limit */ - cpufreq_driver->verify(policy); + ret = cpufreq_driver->verify(policy); + if (ret) { + up(&cpufreq_driver_sem); + return ret; + } + + down(&cpufreq_notifier_sem); /* adjust if neccessary - all reasons */ notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST, @@ -851,7 +865,12 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) /* verify the cpu speed can be set within this limit, which might be different to the first one */ - cpufreq_driver->verify(policy); + ret = cpufreq_driver->verify(policy); + if (ret) { + up(&cpufreq_notifier_sem); + up(&cpufreq_driver_sem); + return ret; + } /* notification of the new policy */ notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY, @@ -879,11 +898,11 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) cpu_cur_freq[policy->cpu] = policy->max; #endif - cpufreq_driver->setpolicy(policy); + ret = cpufreq_driver->setpolicy(policy); up(&cpufreq_driver_sem); - return 0; + return ret; } EXPORT_SYMBOL(cpufreq_set_policy); @@ -968,6 +987,8 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_transition); int cpufreq_register(struct cpufreq_driver *driver_data) { unsigned int ret; + unsigned int i; + struct cpufreq_policy policy; if (cpufreq_driver) return -EBUSY; @@ -979,41 +1000,55 @@ int cpufreq_register(struct cpufreq_driver *driver_data) down(&cpufreq_driver_sem); cpufreq_driver = driver_data; - if (!default_policy.policy) - default_policy.policy = driver_data->policy[0].policy; - if (!default_policy.min) - default_policy.min = driver_data->policy[0].min; - if (!default_policy.max) - default_policy.max = driver_data->policy[0].max; - default_policy.cpu = CPUFREQ_ALL_CPUS; + /* check for a default policy - if it exists, use it on _all_ CPUs*/ + for (i=0; ipolicy[i].policy = default_policy.policy; + if (default_policy.min) + cpufreq_driver->policy[i].min = default_policy.min; + if (default_policy.max) + cpufreq_driver->policy[i].max = default_policy.max; + } - up(&cpufreq_driver_sem); + /* set default policy on all CPUs. Must be called per-CPU and not + * with CPUFREQ_ALL_CPUs as there might be no common policy for all + * CPUs (UltraSPARC etc.) + */ + for (i=0; ipolicy[i].policy; + policy.min = cpufreq_driver->policy[i].min; + policy.max = cpufreq_driver->policy[i].max; + policy.cpu = i; + up(&cpufreq_driver_sem); + ret = cpufreq_set_policy(&policy); + down(&cpufreq_driver_sem); + if (ret) { + cpufreq_driver = NULL; + up(&cpufreq_driver_sem); + return ret; + } + } - ret = cpufreq_set_policy(&default_policy); + up(&cpufreq_driver_sem); cpufreq_proc_init(); #ifdef CONFIG_CPU_FREQ_24_API - down(&cpufreq_driver_sem); - cpu_min_freq = driver_data->cpu_min_freq; - cpu_max_freq = driver_data->policy[0].max_cpu_freq; + down(&cpufreq_driver_sem); + for (i=0; icpu_cur_freq[i]; - } + cpu_min_freq[i] = driver_data->cpu_min_freq[i]; + cpu_max_freq[i] = driver_data->policy[i].max_cpu_freq; + cpu_cur_freq[i] = driver_data->cpu_cur_freq[i]; } up(&cpufreq_driver_sem); cpufreq_sysctl_init(); #endif - if (ret) { - down(&cpufreq_driver_sem); - cpufreq_driver = NULL; - up(&cpufreq_driver_sem); - } - return ret; + return 0; } EXPORT_SYMBOL_GPL(cpufreq_register); @@ -1061,6 +1096,7 @@ int cpufreq_restore(void) { struct cpufreq_policy policy; unsigned int i; + unsigned int ret = 0; if (in_interrupt()) panic("cpufreq_restore() called from interrupt context!"); @@ -1081,10 +1117,10 @@ int cpufreq_restore(void) policy.cpu = i; up(&cpufreq_driver_sem); - cpufreq_set_policy(&policy); + ret += cpufreq_set_policy(&policy); } - return 0; + return ret; } EXPORT_SYMBOL_GPL(cpufreq_restore); #else -- cgit v1.2.3 From 241c59e257d3b1b04f281773d06e2cc94a9d78f5 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 21 Nov 2002 19:07:50 -0800 Subject: [PATCH] Add back in and to linux/interrupt.h needs: asm/system.h: smb_mb() linux/linkage.h: asmlinkage/FASTCALL/etc. --- include/linux/interrupt.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 21023d2807e8..7a0681b9e880 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -3,11 +3,13 @@ #define _LINUX_INTERRUPT_H #include +#include #include #include #include #include #include +#include struct irqaction { void (*handler)(int, void *, struct pt_regs *); -- cgit v1.2.3 From dccb90df2d3fa9e4ac08b7e91f9e900273bbedef Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 21 Nov 2002 19:08:38 -0800 Subject: [PATCH] Split buffer overflow checking out of struct nfs4_compound Here is the a pre-patch in the attempt to get rid of 'struct nfs4_compound', and the associated horrible union in 'struct nfs4_op'. It splits out the fields that are meant to do buffer overflow checking and iovec adjusting on the XDR received/sent data. It moves support for that nto the dedicated structure 'xdr_stream', and the associated functions 'xdr_reserve_space()', 'xdr_inline_decode()'. The patch also expands out the all macros ENCODE_HEAD, ENCODE_TAIL, ADJUST_ARGS and DECODE_HEAD, as well as most of the DECODE_TAILs. --- fs/nfs/nfs4proc.c | 15 +- fs/nfs/nfs4xdr.c | 537 ++++++++++++++++++++------------------------- include/linux/nfs_xdr.h | 4 + include/linux/sunrpc/xdr.h | 87 ++++++++ 4 files changed, 345 insertions(+), 298 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b17c138681eb..e563bdc7e315 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -65,11 +65,6 @@ nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, memset(cp, 0, sizeof(*cp)); cp->ops = ops; cp->server = server; - -#if NFS4_DEBUG - cp->taglen = strlen(tag); - cp->tag = tag; -#endif } static void @@ -373,6 +368,7 @@ nfs4_setup_open(struct nfs4_compound *cp, int flags, struct qstr *name, BUG_ON(cp->flags); + open->op_client_state = cp->server->nfs4_state; open->op_share_access = flags & 3; open->op_opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE; open->op_createmode = NFS4_CREATE_UNCHECKED; @@ -527,6 +523,10 @@ nfs4_setup_rename(struct nfs4_compound *cp, struct qstr *old, struct qstr *new, static void nfs4_setup_renew(struct nfs4_compound *cp) { + struct nfs4_client **client_state = GET_OP(cp, renew); + + *client_state = cp->server->nfs4_state; + OPNUM(cp) = OP_RENEW; cp->req_nops++; cp->renew_index = cp->req_nops; @@ -575,6 +575,7 @@ nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short por sprintf(setclientid->sc_uaddr, "%s.%d.%d", server->ip_addr, port >> 8, port & 255); setclientid->sc_prog = program; setclientid->sc_cb_ident = 0; + setclientid->sc_state = server->nfs4_state; OPNUM(cp) = OP_SETCLIENTID; cp->req_nops++; @@ -583,6 +584,10 @@ nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short por static void nfs4_setup_setclientid_confirm(struct nfs4_compound *cp) { + struct nfs4_client **client_state = GET_OP(cp, setclientid_confirm); + + *client_state = cp->server->nfs4_state; + OPNUM(cp) = OP_SETCLIENTID_CONFIRM; cp->req_nops++; cp->renew_index = cp->req_nops; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 7b6c503e4cc7..3847a6bf0874 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -57,8 +57,6 @@ */ #define COOKIE_MAX 0x7fffffff -#define NFS4_CLIENTID(server) ((server)->nfs4_state->cl_clientid) - #define NFSDBG_FACILITY NFSDBG_XDR /* Mapping from NFS error code to "errno" error code. */ @@ -95,25 +93,22 @@ static struct { * task to translate them into Linux-specific versions which are more * consistent with the style used in NFSv2/v3... */ -#define ENCODE_HEAD \ - u32 *p; -#define ENCODE_TAIL \ - return 0 - #define WRITE32(n) *p++ = htonl(n) #define WRITE64(n) do { \ - *p++ = htonl((u32)((n) >> 32)); \ - *p++ = htonl((u32)(n)); \ + *p++ = htonl((uint32_t)((n) >> 32)); \ + *p++ = htonl((uint32_t)(n)); \ } while (0) #define WRITEMEM(ptr,nbytes) do { \ p = xdr_writemem(p, ptr, nbytes); \ } while (0) -#define RESERVE_SPACE(nbytes) do { BUG_ON(cp->p + XDR_QUADLEN(nbytes) > cp->end); p = cp->p; } while (0) -#define ADJUST_ARGS() cp->p = p +#define RESERVE_SPACE(nbytes) do { \ + p = xdr_reserve_space(xdr, nbytes); \ + BUG_ON(!p); \ +} while (0) static inline -u32 *xdr_writemem(u32 *p, const void *ptr, int nbytes) +uint32_t *xdr_writemem(uint32_t *p, const void *ptr, int nbytes) { int tmp = XDR_QUADLEN(nbytes); if (!tmp) @@ -146,18 +141,18 @@ encode_gid(char *p, gid_t gid) } static int -encode_attrs(struct nfs4_compound *cp, struct iattr *iap) +encode_attrs(struct xdr_stream *xdr, struct iattr *iap) { char owner_name[256]; char owner_group[256]; int owner_namelen = 0; int owner_grouplen = 0; - u32 *q; + uint32_t *p; + uint32_t *q; int len; - u32 bmval0 = 0; - u32 bmval1 = 0; + uint32_t bmval0 = 0; + uint32_t bmval1 = 0; int status; - ENCODE_HEAD; /* * We reserve enough space to write the entire attribute buffer at once. @@ -240,8 +235,6 @@ encode_attrs(struct nfs4_compound *cp, struct iattr *iap) WRITE32(NFS4_SET_TO_SERVER_TIME); } - ADJUST_ARGS(); - /* * Now we backfill the bitmap and the attribute buffer length. */ @@ -256,69 +249,63 @@ out: } static int -encode_access(struct nfs4_compound *cp, struct nfs4_access *access) +encode_access(struct xdr_stream *xdr, struct nfs4_access *access) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(8); WRITE32(OP_ACCESS); WRITE32(access->ac_req_access); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_close(struct nfs4_compound *cp, struct nfs4_close *close) +encode_close(struct xdr_stream *xdr, struct nfs4_close *close) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(20); WRITE32(OP_CLOSE); WRITE32(close->cl_seqid); WRITEMEM(close->cl_stateid, sizeof(nfs4_stateid)); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_commit(struct nfs4_compound *cp, struct nfs4_commit *commit) +encode_commit(struct xdr_stream *xdr, struct nfs4_commit *commit) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(16); WRITE32(OP_COMMIT); WRITE64(commit->co_start); WRITE32(commit->co_len); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_create(struct nfs4_compound *cp, struct nfs4_create *create) +encode_create(struct xdr_stream *xdr, struct nfs4_create *create) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(8); WRITE32(OP_CREATE); WRITE32(create->cr_ftype); - ADJUST_ARGS(); switch (create->cr_ftype) { case NF4LNK: RESERVE_SPACE(4 + create->cr_textlen); WRITE32(create->cr_textlen); WRITEMEM(create->cr_text, create->cr_textlen); - ADJUST_ARGS(); break; case NF4BLK: case NF4CHR: RESERVE_SPACE(8); WRITE32(create->cr_specdata1); WRITE32(create->cr_specdata2); - ADJUST_ARGS(); break; default: @@ -328,74 +315,69 @@ encode_create(struct nfs4_compound *cp, struct nfs4_create *create) RESERVE_SPACE(4 + create->cr_namelen); WRITE32(create->cr_namelen); WRITEMEM(create->cr_name, create->cr_namelen); - ADJUST_ARGS(); - return encode_attrs(cp, create->cr_attrs); + return encode_attrs(xdr, create->cr_attrs); } static int -encode_getattr(struct nfs4_compound *cp, struct nfs4_getattr *getattr) +encode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(16); WRITE32(OP_GETATTR); WRITE32(2); WRITE32(getattr->gt_bmval[0]); WRITE32(getattr->gt_bmval[1]); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_getfh(struct nfs4_compound *cp) +encode_getfh(struct xdr_stream *xdr) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(4); WRITE32(OP_GETFH); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_link(struct nfs4_compound *cp, struct nfs4_link *link) +encode_link(struct xdr_stream *xdr, struct nfs4_link *link) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(8 + link->ln_namelen); WRITE32(OP_LINK); WRITE32(link->ln_namelen); WRITEMEM(link->ln_name, link->ln_namelen); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_lookup(struct nfs4_compound *cp, struct nfs4_lookup *lookup) +encode_lookup(struct xdr_stream *xdr, struct nfs4_lookup *lookup) { int len = lookup->lo_name->len; - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(8 + len); WRITE32(OP_LOOKUP); WRITE32(len); WRITEMEM(lookup->lo_name->name, len); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_open(struct nfs4_compound *cp, struct nfs4_open *open) +encode_open(struct xdr_stream *xdr, struct nfs4_open *open) { static int global_id = 0; int id = global_id++; int status; - ENCODE_HEAD; + uint32_t *p; /* seqid, share_access, share_deny, clientid, ownerlen, owner, opentype */ RESERVE_SPACE(52); @@ -403,24 +385,21 @@ encode_open(struct nfs4_compound *cp, struct nfs4_open *open) WRITE32(0); /* seqid */ WRITE32(open->op_share_access); WRITE32(0); /* for us, share_deny== 0 always */ - WRITE64(NFS4_CLIENTID(cp->server)); + WRITE64(open->op_client_state->cl_clientid); WRITE32(4); WRITE32(id); WRITE32(open->op_opentype); - ADJUST_ARGS(); if (open->op_opentype == NFS4_OPEN_CREATE) { if (open->op_createmode == NFS4_CREATE_EXCLUSIVE) { RESERVE_SPACE(12); WRITE32(open->op_createmode); WRITEMEM(open->op_verifier, sizeof(nfs4_verifier)); - ADJUST_ARGS(); } else if (open->op_attrs) { RESERVE_SPACE(4); WRITE32(open->op_createmode); - ADJUST_ARGS(); - if ((status = encode_attrs(cp, open->op_attrs))) + if ((status = encode_attrs(xdr, open->op_attrs))) return status; } else { @@ -428,7 +407,6 @@ encode_open(struct nfs4_compound *cp, struct nfs4_open *open) WRITE32(open->op_createmode); WRITE32(0); WRITE32(0); - ADJUST_ARGS(); } } @@ -436,15 +414,14 @@ encode_open(struct nfs4_compound *cp, struct nfs4_open *open) WRITE32(NFS4_OPEN_CLAIM_NULL); WRITE32(open->op_name->len); WRITEMEM(open->op_name->name, open->op_name->len); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_open_confirm(struct nfs4_compound *cp, struct nfs4_open_confirm *open_confirm) +encode_open_confirm(struct xdr_stream *xdr, struct nfs4_open_confirm *open_confirm) { - ENCODE_HEAD; + uint32_t *p; /* * Note: In this "stateless" implementation, the OPEN_CONFIRM @@ -454,44 +431,41 @@ encode_open_confirm(struct nfs4_compound *cp, struct nfs4_open_confirm *open_con WRITE32(OP_OPEN_CONFIRM); WRITEMEM(open_confirm->oc_stateid, sizeof(nfs4_stateid)); WRITE32(1); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_putfh(struct nfs4_compound *cp, struct nfs4_putfh *putfh) +encode_putfh(struct xdr_stream *xdr, struct nfs4_putfh *putfh) { int len = putfh->pf_fhandle->size; - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(8 + len); WRITE32(OP_PUTFH); WRITE32(len); WRITEMEM(putfh->pf_fhandle->data, len); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_putrootfh(struct nfs4_compound *cp) +encode_putrootfh(struct xdr_stream *xdr) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(4); WRITE32(OP_PUTROOTFH); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_read(struct nfs4_compound *cp, struct nfs4_read *read, struct rpc_rqst *req) +encode_read(struct xdr_stream *xdr, struct nfs4_read *read, struct rpc_rqst *req) { struct rpc_auth *auth = req->rq_task->tk_auth; int replen; - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(32); WRITE32(OP_READ); @@ -501,26 +475,24 @@ encode_read(struct nfs4_compound *cp, struct nfs4_read *read, struct rpc_rqst *r WRITE32(0); WRITE64(read->rd_offset); WRITE32(read->rd_length); - ADJUST_ARGS(); /* set up reply iovec * toplevel status + taglen + rescount + OP_PUTFH + status * + OP_READ + status + eof + datalen = 9 */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + 9 + XDR_QUADLEN(cp->taglen)) << 2; - req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + replen = (RPC_REPHDRSIZE + auth->au_rslack + 9) << 2; xdr_inline_pages(&req->rq_rcv_buf, replen, read->rd_pages, read->rd_pgbase, read->rd_length); - ENCODE_TAIL; + return 0; } static int -encode_readdir(struct nfs4_compound *cp, struct nfs4_readdir *readdir, struct rpc_rqst *req) +encode_readdir(struct xdr_stream *xdr, struct nfs4_readdir *readdir, struct rpc_rqst *req) { struct rpc_auth *auth = req->rq_task->tk_auth; int replen; - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(40); WRITE32(OP_READDIR); @@ -531,135 +503,124 @@ encode_readdir(struct nfs4_compound *cp, struct nfs4_readdir *readdir, struct rp WRITE32(2); WRITE32(readdir->rd_bmval[0]); WRITE32(readdir->rd_bmval[1]); - ADJUST_ARGS(); /* set up reply iovec * toplevel_status + taglen + rescount + OP_PUTFH + status * + OP_READDIR + status + verifer(2) = 9 */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + 9 + XDR_QUADLEN(cp->taglen)) << 2; - req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + replen = (RPC_REPHDRSIZE + auth->au_rslack + 9) << 2; xdr_inline_pages(&req->rq_rcv_buf, replen, readdir->rd_pages, readdir->rd_pgbase, readdir->rd_count); - ENCODE_TAIL; + return 0; } static int -encode_readlink(struct nfs4_compound *cp, struct nfs4_readlink *readlink, struct rpc_rqst *req) +encode_readlink(struct xdr_stream *xdr, struct nfs4_readlink *readlink, struct rpc_rqst *req) { struct rpc_auth *auth = req->rq_task->tk_auth; int replen; - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(4); WRITE32(OP_READLINK); - ADJUST_ARGS(); /* set up reply iovec * toplevel_status + taglen + rescount + OP_PUTFH + status * + OP_READLINK + status = 7 */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + 7 + XDR_QUADLEN(cp->taglen)) << 2; - req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2; xdr_inline_pages(&req->rq_rcv_buf, replen, readlink->rl_pages, 0, readlink->rl_count); - ENCODE_TAIL; + return 0; } static int -encode_remove(struct nfs4_compound *cp, struct nfs4_remove *remove) +encode_remove(struct xdr_stream *xdr, struct nfs4_remove *remove) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(8 + remove->rm_namelen); WRITE32(OP_REMOVE); WRITE32(remove->rm_namelen); WRITEMEM(remove->rm_name, remove->rm_namelen); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_rename(struct nfs4_compound *cp, struct nfs4_rename *rename) +encode_rename(struct xdr_stream *xdr, struct nfs4_rename *rename) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(8 + rename->rn_oldnamelen); WRITE32(OP_RENAME); WRITE32(rename->rn_oldnamelen); WRITEMEM(rename->rn_oldname, rename->rn_oldnamelen); - ADJUST_ARGS(); RESERVE_SPACE(8 + rename->rn_newnamelen); WRITE32(rename->rn_newnamelen); WRITEMEM(rename->rn_newname, rename->rn_newnamelen); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_renew(struct nfs4_compound *cp) +encode_renew(struct xdr_stream *xdr, struct nfs4_client *client_stateid) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(12); WRITE32(OP_RENEW); - WRITE64(NFS4_CLIENTID(cp->server)); - ADJUST_ARGS(); + WRITE64(client_stateid->cl_clientid); - ENCODE_TAIL; + return 0; } static int -encode_restorefh(struct nfs4_compound *cp) +encode_restorefh(struct xdr_stream *xdr) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(4); WRITE32(OP_RESTOREFH); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_savefh(struct nfs4_compound *cp) +encode_savefh(struct xdr_stream *xdr) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(4); WRITE32(OP_SAVEFH); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_setattr(struct nfs4_compound *cp, struct nfs4_setattr *setattr) +encode_setattr(struct xdr_stream *xdr, struct nfs4_setattr *setattr) { int status; - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(20); WRITE32(OP_SETATTR); WRITEMEM(setattr->st_stateid, sizeof(nfs4_stateid)); - ADJUST_ARGS(); - if ((status = encode_attrs(cp, setattr->st_iap))) + if ((status = encode_attrs(xdr, setattr->st_iap))) return status; - ENCODE_TAIL; + return 0; } static int -encode_setclientid(struct nfs4_compound *cp, struct nfs4_setclientid *setclientid) +encode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid *setclientid) { - u32 total_len; - u32 len1, len2, len3; - ENCODE_HEAD; + uint32_t total_len; + uint32_t len1, len2, len3; + uint32_t *p; len1 = strlen(setclientid->sc_name); len2 = strlen(setclientid->sc_netid); @@ -678,30 +639,28 @@ encode_setclientid(struct nfs4_compound *cp, struct nfs4_setclientid *setclienti WRITE32(len3); WRITEMEM(setclientid->sc_uaddr, len3); WRITE32(setclientid->sc_cb_ident); - ADJUST_ARGS(); - ENCODE_TAIL; + return 0; } static int -encode_setclientid_confirm(struct nfs4_compound *cp) +encode_setclientid_confirm(struct xdr_stream *xdr, struct nfs4_client *client_state) { - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(12 + sizeof(nfs4_verifier)); WRITE32(OP_SETCLIENTID_CONFIRM); - WRITE64(cp->server->nfs4_state->cl_clientid); - WRITEMEM(cp->server->nfs4_state->cl_confirm,sizeof(nfs4_verifier)); - ADJUST_ARGS(); + WRITE64(client_state->cl_clientid); + WRITEMEM(client_state->cl_confirm,sizeof(nfs4_verifier)); - ENCODE_TAIL; + return 0; } static int -encode_write(struct nfs4_compound *cp, struct nfs4_write *write, struct rpc_rqst *req) +encode_write(struct xdr_stream *xdr, struct nfs4_write *write, struct rpc_rqst *req) { struct xdr_buf *sndbuf = &req->rq_snd_buf; - ENCODE_HEAD; + uint32_t *p; RESERVE_SPACE(36); WRITE32(OP_WRITE); @@ -712,20 +671,18 @@ encode_write(struct nfs4_compound *cp, struct nfs4_write *write, struct rpc_rqst WRITE64(write->wr_offset); WRITE32(write->wr_stable_how); WRITE32(write->wr_len); - ADJUST_ARGS(); - sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); xdr_encode_pages(sndbuf, write->wr_pages, write->wr_pgbase, write->wr_len); - ENCODE_TAIL; + return 0; } /* FIXME: this sucks */ static int -encode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) +encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqst *req) { int i, status = 0; - ENCODE_HEAD; + uint32_t *p; dprintk("encode_compound: tag=%.*s\n", (int)cp->taglen, cp->tag); @@ -734,81 +691,80 @@ encode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) WRITEMEM(cp->tag, cp->taglen); WRITE32(NFS4_MINOR_VERSION); WRITE32(cp->req_nops); - ADJUST_ARGS(); for (i = 0; i < cp->req_nops; i++) { switch (cp->ops[i].opnum) { case OP_ACCESS: - status = encode_access(cp, &cp->ops[i].u.access); + status = encode_access(xdr, &cp->ops[i].u.access); break; case OP_CLOSE: - status = encode_close(cp, &cp->ops[i].u.close); + status = encode_close(xdr, &cp->ops[i].u.close); break; case OP_COMMIT: - status = encode_commit(cp, &cp->ops[i].u.commit); + status = encode_commit(xdr, &cp->ops[i].u.commit); break; case OP_CREATE: - status = encode_create(cp, &cp->ops[i].u.create); + status = encode_create(xdr, &cp->ops[i].u.create); break; case OP_GETATTR: - status = encode_getattr(cp, &cp->ops[i].u.getattr); + status = encode_getattr(xdr, &cp->ops[i].u.getattr); break; case OP_GETFH: - status = encode_getfh(cp); + status = encode_getfh(xdr); break; case OP_LINK: - status = encode_link(cp, &cp->ops[i].u.link); + status = encode_link(xdr, &cp->ops[i].u.link); break; case OP_LOOKUP: - status = encode_lookup(cp, &cp->ops[i].u.lookup); + status = encode_lookup(xdr, &cp->ops[i].u.lookup); break; case OP_OPEN: - status = encode_open(cp, &cp->ops[i].u.open); + status = encode_open(xdr, &cp->ops[i].u.open); break; case OP_OPEN_CONFIRM: - status = encode_open_confirm(cp, &cp->ops[i].u.open_confirm); + status = encode_open_confirm(xdr, &cp->ops[i].u.open_confirm); break; case OP_PUTFH: - status = encode_putfh(cp, &cp->ops[i].u.putfh); + status = encode_putfh(xdr, &cp->ops[i].u.putfh); break; case OP_PUTROOTFH: - status = encode_putrootfh(cp); + status = encode_putrootfh(xdr); break; case OP_READ: - status = encode_read(cp, &cp->ops[i].u.read, req); + status = encode_read(xdr, &cp->ops[i].u.read, req); break; case OP_READDIR: - status = encode_readdir(cp, &cp->ops[i].u.readdir, req); + status = encode_readdir(xdr, &cp->ops[i].u.readdir, req); break; case OP_READLINK: - status = encode_readlink(cp, &cp->ops[i].u.readlink, req); + status = encode_readlink(xdr, &cp->ops[i].u.readlink, req); break; case OP_REMOVE: - status = encode_remove(cp, &cp->ops[i].u.remove); + status = encode_remove(xdr, &cp->ops[i].u.remove); break; case OP_RENAME: - status = encode_rename(cp, &cp->ops[i].u.rename); + status = encode_rename(xdr, &cp->ops[i].u.rename); break; case OP_RENEW: - status = encode_renew(cp); + status = encode_renew(xdr, cp->ops[i].u.renew); break; case OP_RESTOREFH: - status = encode_restorefh(cp); + status = encode_restorefh(xdr); break; case OP_SAVEFH: - status = encode_savefh(cp); + status = encode_savefh(xdr); break; case OP_SETATTR: - status = encode_setattr(cp, &cp->ops[i].u.setattr); + status = encode_setattr(xdr, &cp->ops[i].u.setattr); break; case OP_SETCLIENTID: - status = encode_setclientid(cp, &cp->ops[i].u.setclientid); + status = encode_setclientid(xdr, &cp->ops[i].u.setclientid); break; case OP_SETCLIENTID_CONFIRM: - status = encode_setclientid_confirm(cp); + status = encode_setclientid_confirm(xdr, cp->ops[i].u.setclientid_confirm); break; case OP_WRITE: - status = encode_write(cp, &cp->ops[i].u.write, req); + status = encode_write(xdr, &cp->ops[i].u.write, req); break; default: BUG(); @@ -817,7 +773,7 @@ encode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) return status; } - ENCODE_TAIL; + return 0; } /* * END OF "GENERIC" ENCODE ROUTINES. @@ -828,18 +784,14 @@ encode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) * Encode COMPOUND argument */ static int -nfs4_xdr_enc_compound(struct rpc_rqst *req, u32 *p, struct nfs4_compound *cp) +nfs4_xdr_enc_compound(struct rpc_rqst *req, uint32_t *p, struct nfs4_compound *cp) { + struct xdr_stream xdr; int status; - struct xdr_buf *sndbuf = &req->rq_snd_buf; - cp->p = p; - cp->end = (u32 *) ((char *)req->rq_svec[0].iov_base + req->rq_svec[0].iov_len); - status = encode_compound(cp, req); + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + status = encode_compound(&xdr, cp, req); cp->timestamp = jiffies; - - if (!status && !sndbuf->page_len) - req->rq_slen = xdr_adjust_iovec(sndbuf->head, cp->p); return status; } @@ -854,9 +806,6 @@ nfs4_xdr_enc_compound(struct rpc_rqst *req, u32 *p, struct nfs4_compound *cp) * task to translate them into Linux-specific versions which are more * consistent with the style used in NFSv2/v3... */ -#define DECODE_HEAD \ - u32 *p; \ - int status #define DECODE_TAIL \ status = 0; \ out: \ @@ -881,11 +830,13 @@ xdr_error: \ p += XDR_QUADLEN(nbytes); \ } while (0) -#define READ_BUF(nbytes) do { \ - if (nbytes > (u32)((char *)cp->end - (char *)cp->p)) \ - goto xdr_error; \ - p = cp->p; \ - cp->p += XDR_QUADLEN(nbytes); \ +#define READ_BUF(nbytes) do { \ + p = xdr_inline_decode(xdr, nbytes); \ + if (!p) { \ + printk(KERN_WARNING "%s: reply buffer overflowed in line %d.", \ + __FUNCTION__, __LINE__); \ + return -EIO; \ + } \ } while (0) /* @@ -893,7 +844,7 @@ xdr_error: \ * upcall gets in... */ static int -decode_uid(char *p, u32 len, uid_t *uid) +decode_uid(char *p, uint32_t len, uid_t *uid) { *uid = -2; return 0; @@ -904,82 +855,78 @@ decode_uid(char *p, u32 len, uid_t *uid) * upcall gets in... */ static int -decode_gid(char *p, u32 len, gid_t *gid) +decode_gid(char *p, uint32_t len, gid_t *gid) { *gid = -2; return 0; } static int -decode_change_info(struct nfs4_compound *cp, struct nfs4_change_info *cinfo) +decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) { - DECODE_HEAD; + uint32_t *p; READ_BUF(20); READ32(cinfo->atomic); READ64(cinfo->before); READ64(cinfo->after); - - DECODE_TAIL; + return 0; } static int -decode_access(struct nfs4_compound *cp, int nfserr, struct nfs4_access *access) +decode_access(struct xdr_stream *xdr, int nfserr, struct nfs4_access *access) { - u32 supp, acc; - DECODE_HEAD; + uint32_t *p; + uint32_t supp, acc; if (!nfserr) { READ_BUF(8); READ32(supp); READ32(acc); - status = -EIO; if ((supp & ~access->ac_req_access) || (acc & ~supp)) { printk(KERN_NOTICE "NFS: server returned bad bits in access call!\n"); - goto out; + return -EIO; } *access->ac_resp_supported = supp; *access->ac_resp_access = acc; } - - DECODE_TAIL; + return 0; } static int -decode_close(struct nfs4_compound *cp, int nfserr, struct nfs4_close *close) +decode_close(struct xdr_stream *xdr, int nfserr, struct nfs4_close *close) { - DECODE_HEAD; + uint32_t *p; if (!nfserr) { READ_BUF(sizeof(nfs4_stateid)); COPYMEM(close->cl_stateid, sizeof(nfs4_stateid)); } - - DECODE_TAIL; + return 0; } static int -decode_commit(struct nfs4_compound *cp, int nfserr, struct nfs4_commit *commit) +decode_commit(struct xdr_stream *xdr, int nfserr, struct nfs4_commit *commit) { - DECODE_HEAD; + uint32_t *p; if (!nfserr) { READ_BUF(8); COPYMEM(commit->co_verifier->verifier, 8); } - - DECODE_TAIL; + return 0; } static int -decode_create(struct nfs4_compound *cp, int nfserr, struct nfs4_create *create) +decode_create(struct xdr_stream *xdr, int nfserr, struct nfs4_create *create) { - u32 bmlen; - DECODE_HEAD; + uint32_t *p; + uint32_t bmlen; + int status; if (!nfserr) { - if ((status = decode_change_info(cp, create->cr_cinfo))) + if ((status = decode_change_info(xdr, create->cr_cinfo))) goto out; READ_BUF(4); READ32(bmlen); @@ -991,27 +938,28 @@ decode_create(struct nfs4_compound *cp, int nfserr, struct nfs4_create *create) DECODE_TAIL; } -extern u32 nfs4_fattr_bitmap[2]; -extern u32 nfs4_fsinfo_bitmap[2]; -extern u32 nfs4_fsstat_bitmap[2]; -extern u32 nfs4_pathconf_bitmap[2]; +extern uint32_t nfs4_fattr_bitmap[2]; +extern uint32_t nfs4_fsinfo_bitmap[2]; +extern uint32_t nfs4_fsstat_bitmap[2]; +extern uint32_t nfs4_pathconf_bitmap[2]; static int -decode_getattr(struct nfs4_compound *cp, int nfserr, struct nfs4_getattr *getattr) +decode_getattr(struct xdr_stream *xdr, int nfserr, struct nfs4_getattr *getattr) { struct nfs_fattr *nfp = getattr->gt_attrs; struct nfs_fsstat *fsstat = getattr->gt_fsstat; struct nfs_fsinfo *fsinfo = getattr->gt_fsinfo; struct nfs_pathconf *pathconf = getattr->gt_pathconf; - u32 bmlen; - u32 bmval0 = 0; - u32 bmval1 = 0; - u32 attrlen; - u32 dummy32; - u32 len = 0; + uint32_t *p; + uint32_t bmlen; + uint32_t bmval0 = 0; + uint32_t bmval1 = 0; + uint32_t attrlen; + uint32_t dummy32; + uint32_t len = 0; unsigned int type; int fmode = 0; - DECODE_HEAD; + int status; if (nfserr) goto success; @@ -1060,7 +1008,7 @@ decode_getattr(struct nfs4_compound *cp, int nfserr, struct nfs4_getattr *getatt } nfp->type = nfs_type2fmt[type].nfs2type; fmode = nfs_type2fmt[type].mode; - dprintk("read_attrs: type=%d\n", (u32)nfp->type); + dprintk("read_attrs: type=%d\n", (uint32_t)nfp->type); } if (bmval0 & FATTR4_WORD0_CHANGE) { READ_BUF(8); @@ -1250,11 +1198,12 @@ success: } static int -decode_getfh(struct nfs4_compound *cp, int nfserr, struct nfs4_getfh *getfh) +decode_getfh(struct xdr_stream *xdr, int nfserr, struct nfs4_getfh *getfh) { struct nfs_fh *fh = getfh->gf_fhandle; - int len; - DECODE_HEAD; + uint32_t *p; + uint32_t len; + int status; /* Zero handle first to allow comparisons */ memset(fh, 0, sizeof(*fh)); @@ -1273,26 +1222,27 @@ decode_getfh(struct nfs4_compound *cp, int nfserr, struct nfs4_getfh *getfh) } static int -decode_link(struct nfs4_compound *cp, int nfserr, struct nfs4_link *link) +decode_link(struct xdr_stream *xdr, int nfserr, struct nfs4_link *link) { int status = 0; if (!nfserr) - status = decode_change_info(cp, link->ln_cinfo); + status = decode_change_info(xdr, link->ln_cinfo); return status; } static int -decode_open(struct nfs4_compound *cp, int nfserr, struct nfs4_open *open) +decode_open(struct xdr_stream *xdr, int nfserr, struct nfs4_open *open) { - u32 bmlen, delegation_type; - DECODE_HEAD; + uint32_t *p; + uint32_t bmlen, delegation_type; + int status; if (!nfserr) { READ_BUF(sizeof(nfs4_stateid)); COPYMEM(open->op_stateid, sizeof(nfs4_stateid)); - decode_change_info(cp, open->op_cinfo); + decode_change_info(xdr, open->op_cinfo); READ_BUF(8); READ32(*open->op_rflags); @@ -1311,23 +1261,23 @@ decode_open(struct nfs4_compound *cp, int nfserr, struct nfs4_open *open) } static int -decode_open_confirm(struct nfs4_compound *cp, int nfserr, struct nfs4_open_confirm *open_confirm) +decode_open_confirm(struct xdr_stream *xdr, int nfserr, struct nfs4_open_confirm *open_confirm) { - DECODE_HEAD; + uint32_t *p; if (!nfserr) { READ_BUF(sizeof(nfs4_stateid)); COPYMEM(open_confirm->oc_stateid, sizeof(nfs4_stateid)); } - - DECODE_TAIL; + return 0; } static int -decode_read(struct nfs4_compound *cp, int nfserr, struct nfs4_read *read) +decode_read(struct xdr_stream *xdr, int nfserr, struct nfs4_read *read) { - u32 throwaway; - DECODE_HEAD; + uint32_t throwaway; + uint32_t *p; + int status; if (!nfserr) { READ_BUF(8); @@ -1344,23 +1294,22 @@ decode_read(struct nfs4_compound *cp, int nfserr, struct nfs4_read *read) } static int -decode_readdir(struct nfs4_compound *cp, int nfserr, struct rpc_rqst *req, struct nfs4_readdir *readdir) +decode_readdir(struct xdr_stream *xdr, int nfserr, struct rpc_rqst *req, struct nfs4_readdir *readdir) { struct xdr_buf *rcvbuf = &req->rq_rcv_buf; struct page *page = *rcvbuf->pages; unsigned int pglen = rcvbuf->page_len; - u32 *end, *entry; - u32 len, attrlen, word; - int i; - DECODE_HEAD; + uint32_t *end, *entry, *p; + uint32_t len, attrlen, word; + int i; if (!nfserr) { READ_BUF(8); COPYMEM(readdir->rd_resp_verifier, 8); BUG_ON(pglen > PAGE_CACHE_SIZE); - p = (u32 *) kmap(page); - end = (u32 *) ((char *)p + pglen + readdir->rd_pgbase); + p = (uint32_t *) kmap(page); + end = (uint32_t *) ((char *)p + pglen + readdir->rd_pgbase); while (*p++) { entry = p - 1; @@ -1406,7 +1355,7 @@ decode_readdir(struct nfs4_compound *cp, int nfserr, struct rpc_rqst *req, struc kunmap(page); } - DECODE_TAIL; + return 0; short_pkt: printk(KERN_NOTICE "NFS: short packet in readdir reply!\n"); /* truncate listing */ @@ -1419,11 +1368,11 @@ err_unmap: } static int -decode_readlink(struct nfs4_compound *cp, int nfserr, struct rpc_rqst *req, struct nfs4_readlink *readlink) +decode_readlink(struct xdr_stream *xdr, int nfserr, struct rpc_rqst *req, struct nfs4_readlink *readlink) { struct xdr_buf *rcvbuf = &req->rq_rcv_buf; - u32 *strlen; - u32 len; + uint32_t *strlen; + uint32_t len; char *string; if (!nfserr) { @@ -1434,7 +1383,7 @@ decode_readlink(struct nfs4_compound *cp, int nfserr, struct rpc_rqst *req, stru * and and null-terminate the text (the VFS expects * null-termination). */ - strlen = (u32 *) kmap(rcvbuf->pages[0]); + strlen = (uint32_t *) kmap(rcvbuf->pages[0]); len = ntohl(*strlen); if (len > PAGE_CACHE_SIZE - 5) { printk(KERN_WARNING "nfs: server returned giant symlink!\n"); @@ -1451,25 +1400,25 @@ decode_readlink(struct nfs4_compound *cp, int nfserr, struct rpc_rqst *req, stru } static int -decode_remove(struct nfs4_compound *cp, int nfserr, struct nfs4_remove *remove) +decode_remove(struct xdr_stream *xdr, int nfserr, struct nfs4_remove *remove) { int status; status = 0; if (!nfserr) - status = decode_change_info(cp, remove->rm_cinfo); + status = decode_change_info(xdr, remove->rm_cinfo); return status; } static int -decode_rename(struct nfs4_compound *cp, int nfserr, struct nfs4_rename *rename) +decode_rename(struct xdr_stream *xdr, int nfserr, struct nfs4_rename *rename) { int status = 0; if (!nfserr) { - if ((status = decode_change_info(cp, rename->rn_src_cinfo))) + if ((status = decode_change_info(xdr, rename->rn_src_cinfo))) goto out; - if ((status = decode_change_info(cp, rename->rn_dst_cinfo))) + if ((status = decode_change_info(xdr, rename->rn_dst_cinfo))) goto out; } out: @@ -1477,10 +1426,11 @@ out: } static int -decode_setattr(struct nfs4_compound *cp) +decode_setattr(struct xdr_stream *xdr) { - u32 bmlen; - DECODE_HEAD; + uint32_t *p; + uint32_t bmlen; + int status; READ_BUF(4); READ32(bmlen); @@ -1492,17 +1442,17 @@ decode_setattr(struct nfs4_compound *cp) } static int -decode_setclientid(struct nfs4_compound *cp, int nfserr) +decode_setclientid(struct xdr_stream *xdr, int nfserr, struct nfs4_setclientid *setclientid) { - DECODE_HEAD; + uint32_t *p; if (!nfserr) { READ_BUF(8 + sizeof(nfs4_verifier)); - READ64(cp->server->nfs4_state->cl_clientid); - COPYMEM(cp->server->nfs4_state->cl_confirm, sizeof(nfs4_verifier)); + READ64(setclientid->sc_state->cl_clientid); + COPYMEM(setclientid->sc_state->cl_confirm, sizeof(nfs4_verifier)); } else if (nfserr == NFSERR_CLID_INUSE) { - u32 len; + uint32_t len; /* skip netid string */ READ_BUF(4); @@ -1515,13 +1465,14 @@ decode_setclientid(struct nfs4_compound *cp, int nfserr) READ_BUF(len); } - DECODE_TAIL; + return 0; } static int -decode_write(struct nfs4_compound *cp, int nfserr, struct nfs4_write *write) +decode_write(struct xdr_stream *xdr, int nfserr, struct nfs4_write *write) { - DECODE_HEAD; + uint32_t *p; + int status; if (!nfserr) { READ_BUF(16); @@ -1537,11 +1488,12 @@ decode_write(struct nfs4_compound *cp, int nfserr, struct nfs4_write *write) /* FIXME: this sucks */ static int -decode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) +decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqst *req) { - u32 taglen; - u32 opnum, nfserr; - DECODE_HEAD; + uint32_t *p; + uint32_t taglen; + uint32_t opnum, nfserr; + int status; READ_BUF(8); READ32(cp->toplevel_status); @@ -1584,34 +1536,34 @@ decode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) switch (opnum) { case OP_ACCESS: - status = decode_access(cp, nfserr, &cp->ops[cp->nops].u.access); + status = decode_access(xdr, nfserr, &cp->ops[cp->nops].u.access); break; case OP_CLOSE: - status = decode_close(cp, nfserr, &cp->ops[cp->nops].u.close); + status = decode_close(xdr, nfserr, &cp->ops[cp->nops].u.close); break; case OP_COMMIT: - status = decode_commit(cp, nfserr, &cp->ops[cp->nops].u.commit); + status = decode_commit(xdr, nfserr, &cp->ops[cp->nops].u.commit); break; case OP_CREATE: - status = decode_create(cp, nfserr, &cp->ops[cp->nops].u.create); + status = decode_create(xdr, nfserr, &cp->ops[cp->nops].u.create); break; case OP_GETATTR: - status = decode_getattr(cp, nfserr, &cp->ops[cp->nops].u.getattr); + status = decode_getattr(xdr, nfserr, &cp->ops[cp->nops].u.getattr); break; case OP_GETFH: - status = decode_getfh(cp, nfserr, &cp->ops[cp->nops].u.getfh); + status = decode_getfh(xdr, nfserr, &cp->ops[cp->nops].u.getfh); break; case OP_LINK: - status = decode_link(cp, nfserr, &cp->ops[cp->nops].u.link); + status = decode_link(xdr, nfserr, &cp->ops[cp->nops].u.link); break; case OP_LOOKUP: status = 0; break; case OP_OPEN: - status = decode_open(cp, nfserr, &cp->ops[cp->nops].u.open); + status = decode_open(xdr, nfserr, &cp->ops[cp->nops].u.open); break; case OP_OPEN_CONFIRM: - status = decode_open_confirm(cp, nfserr, &cp->ops[cp->nops].u.open_confirm); + status = decode_open_confirm(xdr, nfserr, &cp->ops[cp->nops].u.open_confirm); break; case OP_PUTFH: status = 0; @@ -1620,22 +1572,22 @@ decode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) status = 0; break; case OP_READ: - status = decode_read(cp, nfserr, &cp->ops[cp->nops].u.read); + status = decode_read(xdr, nfserr, &cp->ops[cp->nops].u.read); break; case OP_READDIR: - status = decode_readdir(cp, nfserr, req, &cp->ops[cp->nops].u.readdir); + status = decode_readdir(xdr, nfserr, req, &cp->ops[cp->nops].u.readdir); break; case OP_READLINK: - status = decode_readlink(cp, nfserr, req, &cp->ops[cp->nops].u.readlink); + status = decode_readlink(xdr, nfserr, req, &cp->ops[cp->nops].u.readlink); break; case OP_RESTOREFH: status = 0; break; case OP_REMOVE: - status = decode_remove(cp, nfserr, &cp->ops[cp->nops].u.remove); + status = decode_remove(xdr, nfserr, &cp->ops[cp->nops].u.remove); break; case OP_RENAME: - status = decode_rename(cp, nfserr, &cp->ops[cp->nops].u.rename); + status = decode_rename(xdr, nfserr, &cp->ops[cp->nops].u.rename); break; case OP_RENEW: status = 0; @@ -1644,16 +1596,16 @@ decode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) status = 0; break; case OP_SETATTR: - status = decode_setattr(cp); + status = decode_setattr(xdr); break; case OP_SETCLIENTID: - status = decode_setclientid(cp, nfserr); + status = decode_setclientid(xdr, nfserr, &cp->ops[cp->nops].u.setclientid); break; case OP_SETCLIENTID_CONFIRM: status = 0; break; case OP_WRITE: - status = decode_write(cp, nfserr, &cp->ops[cp->nops].u.write); + status = decode_write(xdr, nfserr, &cp->ops[cp->nops].u.write); break; default: BUG(); @@ -1673,14 +1625,13 @@ decode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) * Decode COMPOUND response */ static int -nfs4_xdr_dec_compound(struct rpc_rqst *rqstp, u32 *p, struct nfs4_compound *cp) +nfs4_xdr_dec_compound(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_compound *cp) { + struct xdr_stream xdr; int status; - cp->p = p; - cp->end = (u32 *) ((u8 *) rqstp->rq_rvec->iov_base + rqstp->rq_rvec->iov_len); - - if ((status = decode_compound(cp, rqstp))) + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + if ((status = decode_compound(&xdr, cp, rqstp))) goto out; status = 0; @@ -1691,10 +1642,10 @@ out: return status; } -u32 * -nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus) +uint32_t * +nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) { - u32 len; + uint32_t len; if (!*p++) { if (!*p) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 6c82048e2acf..970ffa785f78 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -398,6 +398,7 @@ struct nfs4_lookup { }; struct nfs4_open { + struct nfs4_client * op_client_state; /* request */ u32 op_share_access; /* request */ u32 op_opentype; /* request */ u32 op_createmode; /* request */ @@ -472,6 +473,7 @@ struct nfs4_setclientid { char sc_netid[4]; /* request */ char sc_uaddr[24]; /* request */ u32 sc_cb_ident; /* request */ + struct nfs4_client * sc_state; /* response */ }; struct nfs4_write { @@ -504,8 +506,10 @@ struct nfs4_op { struct nfs4_readlink readlink; struct nfs4_remove remove; struct nfs4_rename rename; + struct nfs4_client * renew; struct nfs4_setattr setattr; struct nfs4_setclientid setclientid; + struct nfs4_client * setclientid_confirm; struct nfs4_write write; } u; }; diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 0233988f40b4..74c5260b2343 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -155,6 +155,93 @@ typedef size_t (*skb_read_actor_t)(skb_reader_t *desc, void *to, size_t len); extern void xdr_partial_copy_from_skb(struct xdr_buf *, unsigned int, skb_reader_t *, skb_read_actor_t); +/* + * Provide some simple tools for XDR buffer overflow-checking etc. + */ +struct xdr_stream { + uint32_t *p; /* start of available buffer */ + struct xdr_buf *buf; /* XDR buffer to read/write */ + + uint32_t *end; /* end of available buffer space */ + struct iovec *iov; /* pointer to the current iovec */ +}; + +/* + * Initialize an xdr_stream for encoding data. + * + * Note: at the moment the RPC client only passes the length of our + * scratch buffer in the xdr_buf's header iovec. Previously this + * meant we needed to call xdr_adjust_iovec() after encoding the + * data. With the new scheme, the xdr_stream manages the details + * of the buffer length, and takes care of adjusting the iovec + * length for us. + */ +static inline void +xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p) +{ + struct iovec *iov = buf->head; + + xdr->buf = buf; + xdr->iov = iov; + xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); + buf->len = iov->iov_len = (char *)p - (char *)iov->iov_base; + xdr->p = p; +} + +/* + * Check that we have enough buffer space to encode 'nbytes' more + * bytes of data. If so, update the total xdr_buf length, and + * adjust the length of the current iovec. + */ +static inline uint32_t * +xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) +{ + uint32_t *p = xdr->p; + uint32_t *q; + + /* align nbytes on the next 32-bit boundary */ + nbytes += 3; + nbytes &= ~3; + q = p + (nbytes >> 2); + if (unlikely(q > xdr->end || q < p)) + return NULL; + xdr->p = q; + xdr->iov->iov_len += nbytes; + xdr->buf->len += nbytes; + return p; +} + +/* + * Initialize an xdr_stream for decoding data. + */ +static inline void +xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p) +{ + struct iovec *iov = buf->head; + xdr->buf = buf; + xdr->iov = iov; + xdr->p = p; + xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); +} + +/* + * Check if the input buffer is long enough to enable us to decode + * 'nbytes' more bytes of data starting at the current position. + * If so return the current pointer, then update the current + * position. + */ +static inline uint32_t * +xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) +{ + uint32_t *p = xdr->p; + uint32_t *q = p + XDR_QUADLEN(nbytes); + + if (unlikely(q > xdr->end || q < p)) + return NULL; + xdr->p = q; + return p; +} + #endif /* __KERNEL__ */ #endif /* _SUNRPC_XDR_H_ */ -- cgit v1.2.3 From 89fc0a31eb36a683d331be8f59bef740141fb2af Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Thu, 21 Nov 2002 19:20:01 -0800 Subject: [PATCH] kNFSd - 1 of 2 - Change NFSv4 xdr decoding to cope with separate pages. Now that nfsd uses a list of pages for requests instead of one large buffer, NFSv4 need to know about this. The most interesting part of this is that it is possible that section of a request, like a path name, could span two pages, so we need to be able to kmalloc as little bit of space to copy them into, and make sure they get freed later. --- fs/nfsd/nfs4proc.c | 13 +++- fs/nfsd/nfs4xdr.c | 169 +++++++++++++++++++++++++++++++++++++++------- include/linux/nfsd/xdr4.h | 12 +++- 3 files changed, 167 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 08428f2f22cb..035a09ae8669 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -481,7 +481,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ *p++ = nfssvc_boot.tv_usec; return nfsd_write(rqstp, current_fh, write->wr_offset, - write->wr_buf, write->wr_buflen, &write->wr_how_written); + write->wr_vec, write->wr_vlen, write->wr_buflen, + &write->wr_how_written); } /* This routine never returns NFS_OK! If there are no other errors, it @@ -700,6 +701,16 @@ out: kfree(args->ops); args->ops = args->iops; } + if (args->tmpp) { + kfree(args->tmpp); + args->tmpp = NULL; + } + while (args->to_free) { + struct tmpbuf *tb = args->to_free; + args->to_free = tb->next; + kfree(tb->buf); + kfree(tb); + } fh_put(¤t_fh); fh_put(&save_fh); return status; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index cb73c11be754..6a5d5c61b5ea 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -217,20 +217,92 @@ xdr_error: \ x = (char *)p; \ p += XDR_QUADLEN(nbytes); \ } while (0) +#define SAVEMEM(x,nbytes) do { \ + if (!(x = (p==argp->tmp || p == argp->tmpp) ? \ + savemem(argp, p, nbytes) : \ + (char *)p)) { \ + printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ + goto xdr_error; \ + } \ + p += XDR_QUADLEN(nbytes); \ +} while (0) #define COPYMEM(x,nbytes) do { \ memcpy((x), p, nbytes); \ p += XDR_QUADLEN(nbytes); \ } while (0) #define READ_BUF(nbytes) do { \ - if (nbytes > (u32)((char *)argp->end - (char *)argp->p)) { \ + if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) { \ + p = argp->p; \ + argp->p += XDR_QUADLEN(nbytes); \ + } else if (!(p = read_buf(argp, nbytes))) { \ printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ goto xdr_error; \ } \ - p = argp->p; \ - argp->p += XDR_QUADLEN(nbytes); \ } while (0) +u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes) +{ + /* We want more bytes than seem to be available. + * Maybe we need a new page, may wehave just run out + */ + int avail = (char*)argp->end - (char*)argp->p; + u32 *p; + if (avail + argp->pagelen < nbytes) + return NULL; + if (avail + PAGE_SIZE > nbytes) /* need more than a page !! */ + return NULL; + /* ok, we can do it with the tail plus the next page */ + if (nbytes <= sizeof(argp->tmp)) + p = argp->tmp; + else { + if (argp->tmpp) + kfree(argp->tmpp); + p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL); + if (!p) + return NULL; + + } + memcpy(p, argp->p, avail); + /* step to next page */ + argp->p = page_address(argp->pagelist[0]); + argp->pagelist++; + if (argp->pagelen < PAGE_SIZE) { + argp->end = p + (argp->pagelen>>2); + argp->pagelen = 0; + } else { + argp->end = p + (PAGE_SIZE>>2); + argp->pagelen -= PAGE_SIZE; + } + memcpy(((char*)p)+avail, argp->p, (nbytes - avail)); + argp->p += XDR_QUADLEN(nbytes - avail); + return p; +} + +char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes) +{ + struct tmpbuf *tb; + if (p == argp->tmp) { + p = kmalloc(nbytes, GFP_KERNEL); + if (!p) return NULL; + memcpy(p, argp->tmp, nbytes); + } else { + if (p != argp->tmpp) + BUG(); + argp->tmpp = NULL; + } + tb = kmalloc(sizeof(*tb), GFP_KERNEL); + if (!tb) { + kfree(p); + return NULL; + } + tb->buf = p; + tb->next = argp->to_free; + argp->to_free = tb; + return (char*)p; +} + + static int nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval) { @@ -442,7 +514,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create READ_BUF(4); READ32(create->cr_linklen); READ_BUF(create->cr_linklen); - READMEM(create->cr_linkname, create->cr_linklen); + SAVEMEM(create->cr_linkname, create->cr_linklen); if (check_utf8(create->cr_linkname, create->cr_linklen)) return nfserr_inval; break; @@ -463,7 +535,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create READ_BUF(4); READ32(create->cr_namelen); READ_BUF(create->cr_namelen); - READMEM(create->cr_name, create->cr_namelen); + SAVEMEM(create->cr_name, create->cr_namelen); if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval))) return status; @@ -487,7 +559,7 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link) READ_BUF(4); READ32(link->li_namelen); READ_BUF(link->li_namelen); - READMEM(link->li_name, link->li_namelen); + SAVEMEM(link->li_name, link->li_namelen); if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval))) return status; @@ -502,7 +574,7 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup READ_BUF(4); READ32(lookup->lo_len); READ_BUF(lookup->lo_len); - READMEM(lookup->lo_name, lookup->lo_len); + SAVEMEM(lookup->lo_name, lookup->lo_len); if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent))) return status; @@ -527,7 +599,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) /* owner, open_flag */ READ_BUF(open->op_ownerlen + 4); - READMEM(open->op_owner, open->op_ownerlen); + SAVEMEM(open->op_owner, open->op_ownerlen); READ32(open->op_create); switch (open->op_create) { case NFS4_OPEN_NOCREATE: @@ -562,7 +634,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) READ_BUF(4); READ32(open->op_namelen); READ_BUF(open->op_namelen); - READMEM(open->op_name, open->op_namelen); + SAVEMEM(open->op_name, open->op_namelen); if ((status = check_filename(open->op_name, open->op_namelen, nfserr_inval))) return status; break; @@ -575,7 +647,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) COPYMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t)); READ32(open->op_namelen); READ_BUF(open->op_namelen); - READMEM(open->op_name, open->op_namelen); + SAVEMEM(open->op_name, open->op_namelen); if ((status = check_filename(open->op_name, open->op_namelen, nfserr_inval))) return status; break; @@ -596,7 +668,7 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh) if (putfh->pf_fhlen > NFS4_FHSIZE) goto xdr_error; READ_BUF(putfh->pf_fhlen); - READMEM(putfh->pf_fhval, putfh->pf_fhlen); + SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen); DECODE_TAIL; } @@ -639,7 +711,7 @@ nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove READ_BUF(4); READ32(remove->rm_namelen); READ_BUF(remove->rm_namelen); - READMEM(remove->rm_name, remove->rm_namelen); + SAVEMEM(remove->rm_name, remove->rm_namelen); if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent))) return status; @@ -654,10 +726,10 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename READ_BUF(4); READ32(rename->rn_snamelen); READ_BUF(rename->rn_snamelen + 4); - READMEM(rename->rn_sname, rename->rn_snamelen); + SAVEMEM(rename->rn_sname, rename->rn_snamelen); READ32(rename->rn_tnamelen); READ_BUF(rename->rn_tnamelen); - READMEM(rename->rn_tname, rename->rn_tnamelen); + SAVEMEM(rename->rn_tname, rename->rn_tnamelen); if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent))) return status; if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval))) @@ -701,16 +773,16 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient READ32(setclientid->se_namelen); READ_BUF(setclientid->se_namelen + 8); - READMEM(setclientid->se_name, setclientid->se_namelen); + SAVEMEM(setclientid->se_name, setclientid->se_namelen); READ32(setclientid->se_callback_prog); READ32(setclientid->se_callback_netid_len); READ_BUF(setclientid->se_callback_netid_len + 4); - READMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len); + SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len); READ32(setclientid->se_callback_addr_len); READ_BUF(setclientid->se_callback_addr_len + 4); - READMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len); + SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len); READ32(setclientid->se_callback_ident); DECODE_TAIL; @@ -739,7 +811,7 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify READ_BUF(4); READ32(verify->ve_attrlen); READ_BUF(verify->ve_attrlen); - READMEM(verify->ve_attrval, verify->ve_attrlen); + SAVEMEM(verify->ve_attrval, verify->ve_attrlen); DECODE_TAIL; } @@ -747,6 +819,9 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify static int nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) { + int avail; + int v; + int len; DECODE_HEAD; READ_BUF(sizeof(stateid_t) + 16); @@ -758,8 +833,36 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) goto xdr_error; READ32(write->wr_buflen); - READ_BUF(write->wr_buflen); - READMEM(write->wr_buf, write->wr_buflen); + /* Sorry .. no magic macros for this.. * + * READ_BUF(write->wr_buflen); + * SAVEMEM(write->wr_buf, write->wr_buflen); + */ + avail = (char*)argp->end - (char*)argp->p; + if (avail + argp->pagelen < write->wr_buflen) { + printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); + goto xdr_error; + } + write->wr_vec[0].iov_base = p; + write->wr_vec[0].iov_len = avail; + v = 0; + len = write->wr_buflen; + while (len > write->wr_vec[v].iov_len) { + len -= write->wr_vec[v].iov_len; + v++; + write->wr_vec[v].iov_base = page_address(argp->pagelist[0]); + argp->pagelist++; + if (argp->pagelen >= PAGE_SIZE) { + write->wr_vec[v].iov_len = PAGE_SIZE; + argp->pagelen -= PAGE_SIZE; + } else { + write->wr_vec[v].iov_len = argp->pagelen; + argp->pagelen = 0; + } + } + argp->end = (u32*) (write->wr_vec[v].iov_base + write->wr_vec[v].iov_len); + argp->p = (u32*) (write->wr_vec[v].iov_base + len); + write->wr_vec[v].iov_len = len; + write->wr_vlen = v+1; DECODE_TAIL; } @@ -780,7 +883,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) READ_BUF(4); READ32(argp->taglen); READ_BUF(argp->taglen + 8); - READMEM(argp->tag, argp->taglen); + SAVEMEM(argp->tag, argp->taglen); READ32(argp->minorversion); READ32(argp->opcnt); @@ -1891,13 +1994,29 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoun int status; args->p = p; - args->end = rqstp->rq_argbuf.base + rqstp->rq_argbuf.buflen; + args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len; + args->pagelist = rqstp->rq_arg.pages; + args->pagelen = rqstp->rq_args.page_len; + args->tmpp = NULL; + args->to_free = NULL; args->ops = args->iops; status = nfsd4_decode_compound(args); - if (status && args->ops != args->iops) { - kfree(args->ops); - args->ops = args->iops; + if (status) { + if (args->ops != args->iops) { + kfree(args->ops); + args->ops = args->iops; + } + if (args->tmpp) { + kfree(args->tmpp); + args->tmpp = NULL; + } + while (args->to_free) { + struct tmpbuf *tb = args->to_free; + args->to_free = tb->next; + kfree(tb->buf); + kfree(tb); + } } return !status; } diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index 4238cb04ad90..72b7c03dfc1a 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h @@ -249,7 +249,9 @@ struct nfsd4_write { u64 wr_offset; /* request */ u32 wr_stable_how; /* request */ u32 wr_buflen; /* request */ - char * wr_buf; /* request */ + struct iovec wr_vec[RPCSVC_MAXPAGES]; /* request */ + int wr_vlen; + u32 wr_bytes_written; /* response */ u32 wr_how_written; /* response */ nfs4_verifier wr_verifier; /* response */ @@ -288,6 +290,14 @@ struct nfsd4_compoundargs { /* scratch variables for XDR decode */ u32 * p; u32 * end; + struct page ** pagelist; + int pagelen; + u32 tmp[8]; + u32 * tmpp; + struct tmpbuf { + struct tmpbuf *next; + void *buf; + } *to_free; u32 taglen; char * tag; -- cgit v1.2.3 From dcffe12eacd46017d834cab1be62de65348b091e Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Thu, 21 Nov 2002 19:20:11 -0800 Subject: [PATCH] kNFSd - 2 of 2 - Change NFSv4 reply encoding to cope with multiple pages. This allows NFSv4 responses to cover move than one page. There are still limits though. There can be at most one 'data' response which includes READ, READLINK, READDIR. For these responses, the interesting data goes in a separate page or, for READ, list of pages. All responses before the 'data' response must fit in one page, and all responses after it must also fit in one (separate) page. --- fs/nfsd/nfs4proc.c | 8 +++- fs/nfsd/nfs4xdr.c | 106 ++++++++++++++++++++++++++++++++-------------- include/linux/nfsd/xdr4.h | 5 +++ 3 files changed, 86 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 035a09ae8669..99d89405387d 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -566,11 +566,14 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, fh_init(¤t_fh, NFS4_FHSIZE); fh_init(&save_fh, NFS4_FHSIZE); - resp->p = rqstp->rq_resbuf.buf + 3 + XDR_QUADLEN(args->taglen); - resp->end = rqstp->rq_resbuf.base + rqstp->rq_resbuf.buflen; + resp->xbuf = &rqstp->rq_res; + resp->p = rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len; + resp->p += 3 + XDR_QUADLEN(args->taglen); + resp->end = rqstp->rq_res.head[0].iov_base + PAGE_SIZE; resp->taglen = args->taglen; resp->tag = args->tag; resp->opcnt = 0; + resp->rqstp = rqstp; /* * According to RFC3010, this takes precedence over all other errors. @@ -596,6 +599,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, * failed response to the next operation. If we don't * have enough room, fail with ERR_RESOURCE. */ +/* FIXME - is slack_space *really* words, or bytes??? - neilb */ slack_space = (char *)resp->end - (char *)resp->p; if (slack_space < COMPOUND_SLACK_SPACE + COMPOUND_ERR_SLACK_SPACE) { BUG_ON(slack_space < COMPOUND_ERR_SLACK_SPACE); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 6a5d5c61b5ea..d490011652e1 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1689,27 +1689,52 @@ static int nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read *read) { u32 eof; - unsigned long maxcount; + int v, pn; + unsigned long maxcount, len; ENCODE_HEAD; if (nfserr) return nfserr; + if (resp->xbuf->page_len) + return nfserr_resource; - maxcount = (char *)resp->end - (char *)resp->p - COMPOUND_ERR_SLACK_SPACE - 8; + RESERVE_SPACE(8); /* eof flag and byte count */ + + maxcount = NFSSVC_MAXBLKSIZE; if (maxcount > read->rd_length) maxcount = read->rd_length; - RESERVE_SPACE(maxcount + 8); + + len = maxcount; + v = 0; + while (len > 0) { + pn = resp->rqstp->rq_resused; + svc_take_page(resp->rqstp); + read->rd_iov[v].iov_base = page_address(resp->rqstp->rq_respages[pn]); + read->rd_iov[v].iov_len = len < PAGE_SIZE ? len : PAGE_SIZE; + v++; + len -= PAGE_SIZE; + } + read->rd_vlen = v; nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, - read->rd_offset, (char *)p + 8, &maxcount); + read->rd_offset, + read->rd_iov, read->rd_vlen, + &maxcount); if (nfserr) return nfserr; eof = (read->rd_offset + maxcount >= read->rd_fhp->fh_dentry->d_inode->i_size); WRITE32(eof); WRITE32(maxcount); - p += XDR_QUADLEN(maxcount); ADJUST_ARGS(); + resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; + resp->p = resp->xbuf->tail[0].iov_base; + resp->xbuf->page_len = maxcount; + if (maxcount&3) { + *(resp->p)++ = 0; + resp->xbuf->tail[0].iov_base += maxcount&3; + resp->xbuf->tail[0].iov_len = 4 - (maxcount&3); + } return 0; } @@ -1717,13 +1742,24 @@ static int nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readlink *readlink) { int maxcount; + char *page; ENCODE_HEAD; if (nfserr) return nfserr; + if (resp->xbuf->page_len) + return nfserr_resource; - maxcount = (char *)resp->end - (char *)resp->p - COMPOUND_ERR_SLACK_SPACE - 4; - RESERVE_SPACE(maxcount + 4); + svc_take_page(resp->rqstp); + page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); + + svc_take_page(resp->rqstp); + resp->xbuf->tail[0].iov_base = + page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); + resp->xbuf->tail[0].iov_len = 0; + + maxcount = PAGE_SIZE; + RESERVE_SPACE(4); /* * XXX: By default, the ->readlink() VFS op will truncate symlinks @@ -1731,13 +1767,20 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r * not, one easy fix is: if ->readlink() precisely fills the buffer, * assume that truncation occured, and return NFS4ERR_RESOURCE. */ - nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, (char *)p + 4, &maxcount); + nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount); if (nfserr) return nfserr; WRITE32(maxcount); - p += XDR_QUADLEN(maxcount); ADJUST_ARGS(); + resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; + resp->p = resp->xbuf->tail[0].iov_base; + resp->xbuf->page_len = maxcount; + if (maxcount&3) { + *(resp->p)++ = 0; + resp->xbuf->tail[0].iov_base += maxcount&3; + resp->xbuf->tail[0].iov_len = 4 - (maxcount&3); + } return 0; } @@ -1746,14 +1789,22 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re { int maxcount; loff_t offset; + u32 *page; ENCODE_HEAD; if (nfserr) return nfserr; + if (resp->xbuf->page_len) + return nfserr_resource; - RESERVE_SPACE(16); + RESERVE_SPACE(8); /* verifier */ + + /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ + WRITE32(0); + WRITE32(0); + ADJUST_ARGS(); - maxcount = (char *)resp->end - (char *)p - COMPOUND_ERR_SLACK_SPACE; + maxcount = PAGE_SIZE; if (maxcount > readdir->rd_maxcount) maxcount = readdir->rd_maxcount; @@ -1766,13 +1817,11 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re if (maxcount < 0) return nfserr_readdir_nospc; - /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ - WRITE32(0); - WRITE32(0); - + svc_take_page(resp->rqstp); + page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); readdir->common.err = 0; readdir->buflen = maxcount; - readdir->buffer = p; + readdir->buffer = page; readdir->offset = NULL; offset = readdir->rd_cookie; @@ -1781,7 +1830,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re &readdir->common, nfsd4_encode_dirent); if (nfserr == nfs_ok && readdir->common.err == nfserr_readdir_nospc && - readdir->buffer == p) + readdir->buffer == page) nfserr = nfserr_readdir_nospc; if (!nfserr) { if (readdir->offset) @@ -1790,7 +1839,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re p = readdir->buffer; *p++ = 0; /* no more entries */ *p++ = htonl(readdir->common.err == nfserr_eof); - ADJUST_ARGS(); + resp->xbuf->page_len = ((char*)p) - (char*)page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); } return nfserr; } @@ -1971,17 +2020,6 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) * END OF "GENERIC" ENCODE ROUTINES. */ -static inline int -xdr_ressize_check(struct svc_rqst *rqstp, u32 *p) -{ - struct svc_buf *buf = &rqstp->rq_resbuf; - - buf->len = p - buf->base; - dprintk("nfsd: ressize_check p %p base %p len %d\n", - p, buf->base, buf->buflen); - return (buf->len <= buf->buflen); -} - int nfs4svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy) { @@ -1996,7 +2034,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoun args->p = p; args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len; args->pagelist = rqstp->rq_arg.pages; - args->pagelen = rqstp->rq_args.page_len; + args->pagelen = rqstp->rq_arg.page_len; args->tmpp = NULL; args->to_free = NULL; args->ops = args->iops; @@ -2027,12 +2065,18 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compound /* * All that remains is to write the tag and operation count... */ + struct iovec *iov; *p++ = htonl(resp->taglen); memcpy(p, resp->tag, resp->taglen); p += XDR_QUADLEN(resp->taglen); *p++ = htonl(resp->opcnt); - BUG_ON(!xdr_ressize_check(rqstp, resp->p)); + if (rqstp->rq_res.page_len) + iov = &rqstp->rq_res.tail[0]; + else + iov = &rqstp->rq_res.head[0]; + iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base; + BUG_ON(iov->iov_len > PAGE_SIZE); return 1; } diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index 72b7c03dfc1a..8359679908ab 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h @@ -173,6 +173,9 @@ struct nfsd4_read { stateid_t rd_stateid; /* request */ u64 rd_offset; /* request */ u32 rd_length; /* request */ + struct iovec rd_iov[RPCSVC_MAXPAGES]; + int rd_vlen; + struct svc_rqst *rd_rqstp; /* response */ struct svc_fh * rd_fhp; /* response */ }; @@ -311,6 +314,8 @@ struct nfsd4_compoundres { /* scratch variables for XDR encode */ u32 * p; u32 * end; + struct xdr_buf * xbuf; + struct svc_rqst * rqstp; u32 taglen; char * tag; -- cgit v1.2.3 From 443bfb9d84810e868e8c7e81bdb5746819ca1916 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 21 Nov 2002 19:31:14 -0800 Subject: [PATCH] Make inode_ops->setxattr value parameter const Patch from Andreas Gruenbacher The setxattr inode operation is defined like this in 2.4 and 2.5: int (*setxattr) (struct dentry *dentry, const char *name, void *value, size_t size, int flags); the original type of the value parameter was `const void *'; the const obviously has been lost at some point. The definition should be: int (*setxattr) (struct dentry *dentry, const char *name, const void *value, size_t size, int flags); --- fs/ext2/xattr.c | 2 +- fs/ext2/xattr.h | 2 +- fs/ext3/xattr.c | 2 +- fs/ext3/xattr.h | 2 +- fs/jfs/jfs_xattr.h | 6 +++--- fs/jfs/xattr.c | 6 +++--- include/linux/fs.h | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 30d8ecdc17c7..5a4592b0b7b6 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -230,7 +230,7 @@ ext2_listxattr(struct dentry *dentry, char *buffer, size_t size) */ int ext2_setxattr(struct dentry *dentry, const char *name, - void *value, size_t size, int flags) + const void *value, size_t size, int flags) { struct ext2_xattr_handler *handler; struct inode *inode = dentry->d_inode; diff --git a/fs/ext2/xattr.h b/fs/ext2/xattr.h index 4414b30d9185..612bfc626af5 100644 --- a/fs/ext2/xattr.h +++ b/fs/ext2/xattr.h @@ -67,7 +67,7 @@ struct ext2_xattr_handler { extern int ext2_xattr_register(int, struct ext2_xattr_handler *); extern void ext2_xattr_unregister(int, struct ext2_xattr_handler *); -extern int ext2_setxattr(struct dentry *, const char *, void *, size_t, int); +extern int ext2_setxattr(struct dentry *, const char *, const void *, size_t, int); extern ssize_t ext2_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t ext2_listxattr(struct dentry *, char *, size_t); extern int ext2_removexattr(struct dentry *, const char *); diff --git a/fs/ext3/xattr.c b/fs/ext3/xattr.c index 75d4659507f4..7b42a75dd579 100644 --- a/fs/ext3/xattr.c +++ b/fs/ext3/xattr.c @@ -223,7 +223,7 @@ ext3_listxattr(struct dentry *dentry, char *buffer, size_t size) */ int ext3_setxattr(struct dentry *dentry, const char *name, - void *value, size_t size, int flags) + const void *value, size_t size, int flags) { struct ext3_xattr_handler *handler; struct inode *inode = dentry->d_inode; diff --git a/fs/ext3/xattr.h b/fs/ext3/xattr.h index 1e3a5149207e..f8a6e6dac22f 100644 --- a/fs/ext3/xattr.h +++ b/fs/ext3/xattr.h @@ -66,7 +66,7 @@ struct ext3_xattr_handler { extern int ext3_xattr_register(int, struct ext3_xattr_handler *); extern void ext3_xattr_unregister(int, struct ext3_xattr_handler *); -extern int ext3_setxattr(struct dentry *, const char *, void *, size_t, int); +extern int ext3_setxattr(struct dentry *, const char *, const void *, size_t, int); extern ssize_t ext3_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t ext3_listxattr(struct dentry *, char *, size_t); extern int ext3_removexattr(struct dentry *, const char *); diff --git a/fs/jfs/jfs_xattr.h b/fs/jfs/jfs_xattr.h index 6cb1c8b70e89..acc196e66c70 100644 --- a/fs/jfs/jfs_xattr.h +++ b/fs/jfs/jfs_xattr.h @@ -52,9 +52,9 @@ struct jfs_ea_list { #define END_EALIST(ealist) \ ((struct jfs_ea *) (((char *) (ealist)) + EALIST_SIZE(ealist))) -extern int __jfs_setxattr(struct inode *, const char *, void *, size_t, - int); -extern int jfs_setxattr(struct dentry *, const char *, void *, size_t, +extern int __jfs_setxattr(struct inode *, const char *, const void *, size_t, + int); +extern int jfs_setxattr(struct dentry *, const char *, const void *, size_t, int); extern ssize_t __jfs_getxattr(struct inode *, const char *, void *, size_t); extern ssize_t jfs_getxattr(struct dentry *, const char *, void *, size_t); diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 873ef6fbf8bf..a9c455de618b 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -706,7 +706,7 @@ static int can_set_system_xattr(struct inode *inode, const char *name, } static int can_set_xattr(struct inode *inode, const char *name, - void *value, size_t value_len) + const void *value, size_t value_len) { if (IS_RDONLY(inode)) return -EROFS; @@ -735,7 +735,7 @@ static int can_set_xattr(struct inode *inode, const char *name, #endif } -int __jfs_setxattr(struct inode *inode, const char *name, void *value, +int __jfs_setxattr(struct inode *inode, const char *name, const void *value, size_t value_len, int flags) { struct jfs_ea_list *ealist; @@ -874,7 +874,7 @@ int __jfs_setxattr(struct inode *inode, const char *name, void *value, return rc; } -int jfs_setxattr(struct dentry *dentry, const char *name, void *value, +int jfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t value_len, int flags) { if (value == NULL) { /* empty EA, do not remove */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 86c370cc3355..5183f5f7c91c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -781,7 +781,7 @@ struct inode_operations { int (*permission) (struct inode *, int); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *); - int (*setxattr) (struct dentry *, const char *, void *, size_t, int); + int (*setxattr) (struct dentry *, const char *,const void *,size_t,int); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t); int (*removexattr) (struct dentry *, const char *); -- cgit v1.2.3 From 5fa9d488fb0dbc8b5583be8e59d8b3091fbcb5e9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 21 Nov 2002 19:32:03 -0800 Subject: [PATCH] Fix busy-wait with writeback to large queues blk_congestion_wait() is a utility function which various callers use to throttle themselves to the rate at which the IO system can retire writes. The current implementation refuses to wait if no queues are "congested" (>75% of requests are in flight). That doesn't work if the queue is so huge that it can hold more than 40% (dirty_ratio) of memory. The queue simply cannot enter congestion because the VM refuses to allow more than 40% of memory to be dirtied. (This spin could happen with a lot of normal-sized queues too) So this patch simply changes blk_congestion_wait() to throttle even if there are no congested queues. It will cause the caller to sleep until someone puts back a write request against any queue. (Nobody uses blk_congestion_wait for read congestion). The patch adds new state to backing_dev_info->state: a couple of flags which indicate whether there are _any_ reads or writes in flight against that queue. This was added to prevent blk_congestion_wait() from taking a nap when there are no writes at all in flight. But the "are there any reads" info could be used to defer background writeout from pdflush, to reduce read-vs-write competition. We'll see. Because the large request queues have made a fundamental change: blocking in get_request_wait() has been the main form of VM throttling for years. But with large queues it doesn't work any more - all throttling happens in blk_congestion_wait(). Also, change io_schedule_timeout() to propagate the schedule_timeout() return value. I was using that in some debug code, but it should have been like that from day one. --- drivers/block/ll_rw_blk.c | 52 +++++++++++++++++++++++++++++++++++++++++---- include/linux/backing-dev.h | 12 +++++++++++ include/linux/sched.h | 2 +- kernel/sched.c | 6 ++++-- 4 files changed, 65 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 336f0eb49cd8..26a311728160 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -56,6 +56,7 @@ int blk_nohighio = 0; static struct congestion_state { wait_queue_head_t wqh; atomic_t nr_congested_queues; + atomic_t nr_active_queues; } congestion_states[2]; /* @@ -86,6 +87,11 @@ static inline int queue_congestion_off_threshold(void) return ret; } +/* + * A queue has just exitted congestion. Note this in the global counter of + * congested queues, and wake up anyone who was waiting for requests to be + * put back. + */ static void clear_queue_congested(request_queue_t *q, int rw) { enum bdi_state bit; @@ -99,6 +105,10 @@ static void clear_queue_congested(request_queue_t *q, int rw) wake_up(&cs->wqh); } +/* + * A queue has just entered congestion. Flag that in the queue's VM-visible + * state flags and increment the global gounter of congested queues. + */ static void set_queue_congested(request_queue_t *q, int rw) { enum bdi_state bit; @@ -109,6 +119,34 @@ static void set_queue_congested(request_queue_t *q, int rw) atomic_inc(&congestion_states[rw].nr_congested_queues); } +/* + * A queue has just put back its last read or write request and has fallen + * idle. + */ +static void clear_queue_active(request_queue_t *q, int rw) +{ + enum bdi_state bit; + + bit = (rw == WRITE) ? BDI_write_active : BDI_read_active; + + if (test_and_clear_bit(bit, &q->backing_dev_info.state)) + atomic_dec(&congestion_states[rw].nr_active_queues); +} + +/* + * A queue has just taken its first read or write request and has become + * active. + */ +static void set_queue_active(request_queue_t *q, int rw) +{ + enum bdi_state bit; + + bit = (rw == WRITE) ? BDI_write_active : BDI_read_active; + + if (!test_and_set_bit(bit, &q->backing_dev_info.state)) + atomic_inc(&congestion_states[rw].nr_active_queues); +} + /** * blk_get_backing_dev_info - get the address of a queue's backing_dev_info * @dev: device @@ -1252,6 +1290,8 @@ static struct request *get_request(request_queue_t *q, int rw) rq = blkdev_free_rq(&rl->free); list_del_init(&rq->queuelist); rq->ref_count = 1; + if (rl->count == queue_nr_requests) + set_queue_active(q, rw); rl->count--; if (rl->count < queue_congestion_on_threshold()) set_queue_congested(q, rw); @@ -1484,6 +1524,8 @@ void __blk_put_request(request_queue_t *q, struct request *req) rl->count++; if (rl->count >= queue_congestion_off_threshold()) clear_queue_congested(q, rw); + if (rl->count == queue_nr_requests) + clear_queue_active(q, rw); if (rl->count >= batch_requests && waitqueue_active(&rl->wait)) wake_up(&rl->wait); } @@ -1512,19 +1554,20 @@ void blk_put_request(struct request *req) * @timeout: timeout in jiffies * * Waits for up to @timeout jiffies for a queue (any queue) to exit congestion. - * If no queues are congested then just return, in the hope that the caller - * will submit some more IO. + * If no queues are congested then just wait for the next request to be + * returned. */ void blk_congestion_wait(int rw, long timeout) { DEFINE_WAIT(wait); struct congestion_state *cs = &congestion_states[rw]; - if (atomic_read(&cs->nr_congested_queues) == 0) + if (!atomic_read(&cs->nr_active_queues)) return; + blk_run_queues(); prepare_to_wait(&cs->wqh, &wait, TASK_UNINTERRUPTIBLE); - if (atomic_read(&cs->nr_congested_queues) != 0) + if (atomic_read(&cs->nr_active_queues)) io_schedule_timeout(timeout); finish_wait(&cs->wqh, &wait); } @@ -2157,6 +2200,7 @@ int __init blk_dev_init(void) for (i = 0; i < ARRAY_SIZE(congestion_states); i++) { init_waitqueue_head(&congestion_states[i].wqh); atomic_set(&congestion_states[i].nr_congested_queues, 0); + atomic_set(&congestion_states[i].nr_active_queues, 0); } return 0; }; diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 94c93c9c5f66..55218964e7ef 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -17,6 +17,8 @@ enum bdi_state { BDI_pdflush, /* A pdflush thread is working this device */ BDI_write_congested, /* The write queue is getting full */ BDI_read_congested, /* The read queue is getting full */ + BDI_write_active, /* There are one or more queued writes */ + BDI_read_active, /* There are one or more queued reads */ BDI_unused, /* Available bits start here */ }; @@ -42,4 +44,14 @@ static inline int bdi_write_congested(struct backing_dev_info *bdi) return test_bit(BDI_write_congested, &bdi->state); } +static inline int bdi_read_active(struct backing_dev_info *bdi) +{ + return test_bit(BDI_read_active, &bdi->state); +} + +static inline int bdi_write_active(struct backing_dev_info *bdi) +{ + return test_bit(BDI_write_active, &bdi->state); +} + #endif /* _LINUX_BACKING_DEV_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 7946bd8cb0ad..facb0f80d0a8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -150,7 +150,7 @@ extern void show_stack(unsigned long *stack); extern void show_regs(struct pt_regs *); void io_schedule(void); -void io_schedule_timeout(long timeout); +long io_schedule_timeout(long timeout); extern void cpu_init (void); extern void trap_init(void); diff --git a/kernel/sched.c b/kernel/sched.c index 32a86a59d462..63efff4f8d68 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1745,13 +1745,15 @@ void io_schedule(void) atomic_dec(&rq->nr_iowait); } -void io_schedule_timeout(long timeout) +long io_schedule_timeout(long timeout) { struct runqueue *rq = this_rq(); + long ret; atomic_inc(&rq->nr_iowait); - schedule_timeout(timeout); + ret = schedule_timeout(timeout); atomic_dec(&rq->nr_iowait); + return ret; } /** -- cgit v1.2.3 From 53bf7bef7cc35c02054aae79d838ace99dcea35b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 21 Nov 2002 19:32:14 -0800 Subject: [PATCH] Remove mapping->vm_writeback The vm_writeback address_space operation was designed to provide the VM with a "clustered writeout" capability. It allowed the filesystem to perform more intelligent writearound decisions when the VM was trying to clean a particular page. I can't say I ever saw any real benefit from this - not much writeout actually happens on that path - quite a lot of work has gone into minimising it actually. The default ->vm_writeback a_op which I provided wrote back the pages in ->dirty_pages order. But there is one scenario in which this causes problems - writing a single 4G file with mem=4G. We end up with all of ZONE_NORMAL full of dirty pages, but all writeback effort is against highmem pages. (Because there is about 1.5G of dirty memory total). Net effect: the machine stalls ZONE_NORMAL allocation attempts until the ->dirty_pages writeback advances onto ZONE_NORMAL pages. This can be fixed most sweetly with additional radix-tree infrastructure which will be quite complex. Later. So this patch dumps it all, and goes back to using writepage against individual pages as they come off the LRU. --- Documentation/filesystems/Locking | 10 ------ fs/block_dev.c | 1 - fs/ext2/inode.c | 1 - fs/mpage.c | 11 +------ include/linux/fs.h | 6 ---- include/linux/pagevec.h | 1 - mm/page-writeback.c | 65 --------------------------------------- mm/page_io.c | 15 --------- mm/shmem.c | 9 ------ mm/swap.c | 29 ----------------- mm/vmscan.c | 28 ++++++----------- 11 files changed, 10 insertions(+), 166 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 2351591b275c..b69c40cc5748 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -135,7 +135,6 @@ prototypes: int (*readpage)(struct file *, struct page *); int (*sync_page)(struct page *); int (*writepages)(struct address_space *, int *nr_to_write); - int (*vm_writeback)(struct page *, int *nr_to_write); int (*set_page_dirty)(struct page *page); int (*prepare_write)(struct file *, struct page *, unsigned, unsigned); int (*commit_write)(struct file *, struct page *, unsigned, unsigned); @@ -153,7 +152,6 @@ readpage: no yes, unlocks readpages: no sync_page: no maybe writepages: no -vm_writeback: no yes set_page_dirty no no prepare_write: no yes commit_write: no yes @@ -190,14 +188,6 @@ written. The address_space implementation may write more (or less) pages than *nr_to_write asks for, but it should try to be reasonably close. If nr_to_write is NULL, all dirty pages must be written. - ->vm_writeback() is called from the VM. The address_space should -start I/O against at least *nr_to_write pages, including the passed page. As -each page is written its PG_launder flag must be set (inside the page lock). - - The vm_writeback() function is provided so that filesytems can perform -clustered writeback around the page which the VM is trying to clean. -If a_ops.vm_writeback is NULL the VM will fall back to single-page writepage(). - ->set_page_dirty() is called from various places in the kernel when the target page is marked as needing writeback. It may be called under spinlock (it cannot block) and is sometimes called with the page diff --git a/fs/block_dev.c b/fs/block_dev.c index ee5d3161ee7b..d024df4bd465 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -758,7 +758,6 @@ struct address_space_operations def_blk_aops = { .prepare_write = blkdev_prepare_write, .commit_write = blkdev_commit_write, .writepages = generic_writepages, - .vm_writeback = generic_vm_writeback, .direct_IO = blkdev_direct_IO, }; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 0db46303da8b..7fe83a044386 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -655,7 +655,6 @@ struct address_space_operations ext2_aops = { .bmap = ext2_bmap, .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, - .vm_writeback = generic_vm_writeback, }; /* diff --git a/fs/mpage.c b/fs/mpage.c index 6a93622217e8..05622a8e515a 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -614,12 +614,6 @@ mpage_writepages(struct address_space *mapping, bio = mpage_writepage(bio, page, get_block, &last_block_in_bio, &ret); } - if ((current->flags & PF_MEMALLOC) && - !PageActive(page) && PageLRU(page)) { - if (!pagevec_add(&pvec, page)) - pagevec_deactivate_inactive(&pvec); - page = NULL; - } if (ret || (--(wbc->nr_to_write) <= 0)) done = 1; if (wbc->nonblocking && bdi_write_congested(bdi)) { @@ -630,16 +624,13 @@ mpage_writepages(struct address_space *mapping, } else { unlock_page(page); } - - if (page) - page_cache_release(page); + page_cache_release(page); write_lock(&mapping->page_lock); } /* * Leave any remaining dirty pages on ->io_pages */ write_unlock(&mapping->page_lock); - pagevec_deactivate_inactive(&pvec); if (bio) mpage_bio_submit(WRITE, bio); return ret; diff --git a/include/linux/fs.h b/include/linux/fs.h index 5183f5f7c91c..f39d21e5bcd9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -285,9 +285,6 @@ struct address_space_operations { /* Write back some dirty pages from this mapping. */ int (*writepages)(struct address_space *, struct writeback_control *); - /* Perform a writeback as a memory-freeing operation. */ - int (*vm_writeback)(struct page *, struct writeback_control *); - /* Set a page dirty */ int (*set_page_dirty)(struct page *page); @@ -1256,9 +1253,6 @@ extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin); extern loff_t remote_llseek(struct file *file, loff_t offset, int origin); extern int generic_file_open(struct inode * inode, struct file * filp); -extern int generic_vm_writeback(struct page *page, - struct writeback_control *wbc); - static inline void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc, read_actor_t actor) diff --git a/include/linux/pagevec.h b/include/linux/pagevec.h index dbfa3ab99699..2a332eed3d82 100644 --- a/include/linux/pagevec.h +++ b/include/linux/pagevec.h @@ -21,7 +21,6 @@ void __pagevec_release_nonlru(struct pagevec *pvec); void __pagevec_free(struct pagevec *pvec); void __pagevec_lru_add(struct pagevec *pvec); void __pagevec_lru_add_active(struct pagevec *pvec); -void pagevec_deactivate_inactive(struct pagevec *pvec); void pagevec_strip(struct pagevec *pvec); unsigned int pagevec_lookup(struct pagevec *pvec, struct address_space *mapping, pgoff_t start, unsigned int nr_pages); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index b0d4c3cc7fc4..91b16e7ea360 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -390,71 +390,6 @@ static int __init page_writeback_init(void) } module_init(page_writeback_init); -/* - * A library function, which implements the vm_writeback a_op. It's fairly - * lame at this time. The idea is: the VM wants to liberate this page, - * so we pass the page to the address_space and give the fs the opportunity - * to write out lots of pages around this one. It allows extent-based - * filesytems to do intelligent things. It lets delayed-allocate filesystems - * perform better file layout. It lets the address_space opportunistically - * write back disk-contiguous pages which are in other zones. - * - * FIXME: the VM wants to start I/O against *this* page. Because its zone - * is under pressure. But this function may start writeout against a - * totally different set of pages. Unlikely to be a huge problem, but if it - * is, we could just writepage the page if it is still (PageDirty && - * !PageWriteback) (See below). - * - * Another option is to just reposition page->mapping->dirty_pages so we - * *know* that the page will be written. That will work fine, but seems - * unpleasant. (If the page is not for-sure on ->dirty_pages we're dead). - * Plus it assumes that the address_space is performing writeback in - * ->dirty_pages order. - * - * So. The proper fix is to leave the page locked-and-dirty and to pass - * it all the way down. - */ -int generic_vm_writeback(struct page *page, struct writeback_control *wbc) -{ - struct inode *inode = page->mapping->host; - - /* - * We don't own this inode, and we don't want the address_space - * vanishing while writeback is walking its pages. - */ - inode = igrab(inode); - unlock_page(page); - - if (inode) { - do_writepages(inode->i_mapping, wbc); - - /* - * This iput() will internally call ext2_discard_prealloc(), - * which is rather bogus. But there is no other way of - * dropping our ref to the inode. However, there's no harm - * in dropping the prealloc, because there probably isn't any. - * Just a waste of cycles. - */ - iput(inode); -#if 0 - if (!PageWriteback(page) && PageDirty(page)) { - lock_page(page); - if (!PageWriteback(page)&&test_clear_page_dirty(page)) { - int ret; - - ret = page->mapping->a_ops->writepage(page); - if (ret == -EAGAIN) - __set_page_dirty_nobuffers(page); - } else { - unlock_page(page); - } - } -#endif - } - return 0; -} -EXPORT_SYMBOL(generic_vm_writeback); - int do_writepages(struct address_space *mapping, struct writeback_control *wbc) { if (mapping->a_ops->writepages) diff --git a/mm/page_io.c b/mm/page_io.c index 6d8db6c98d8c..5d3bfdce334f 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -128,23 +128,8 @@ int swap_readpage(struct file *file, struct page *page) out: return ret; } -/* - * swapper_space doesn't have a real inode, so it gets a special vm_writeback() - * so we don't need swap special cases in generic_vm_writeback(). - * - * Swap pages are !PageLocked and PageWriteback while under writeout so that - * memory allocators will throttle against them. - */ -static int swap_vm_writeback(struct page *page, struct writeback_control *wbc) -{ - struct address_space *mapping = page->mapping; - - unlock_page(page); - return generic_writepages(mapping, wbc); -} struct address_space_operations swap_aops = { - .vm_writeback = swap_vm_writeback, .writepage = swap_writepage, .readpage = swap_readpage, .sync_page = block_sync_page, diff --git a/mm/shmem.c b/mm/shmem.c index 2325743e2561..215f7d3caba7 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -717,14 +717,6 @@ static int shmem_writepages(struct address_space *mapping, struct writeback_cont return 0; } -static int shmem_vm_writeback(struct page *page, struct writeback_control *wbc) -{ - clear_page_dirty(page); - if (shmem_writepage(page) < 0) - set_page_dirty(page); - return 0; -} - /* * shmem_getpage - either get the page from swap or allocate a new one * @@ -1811,7 +1803,6 @@ static void destroy_inodecache(void) static struct address_space_operations shmem_aops = { .writepage = shmem_writepage, .writepages = shmem_writepages, - .vm_writeback = shmem_vm_writeback, .set_page_dirty = __set_page_dirty_nobuffers, #ifdef CONFIG_TMPFS .readpage = shmem_readpage, diff --git a/mm/swap.c b/mm/swap.c index e921b1c14626..7a8956aa811b 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -199,35 +199,6 @@ void __pagevec_release_nonlru(struct pagevec *pvec) pagevec_reinit(pvec); } -/* - * Move all the inactive pages to the head of the inactive list and release - * them. Reinitialises the caller's pagevec. - */ -void pagevec_deactivate_inactive(struct pagevec *pvec) -{ - int i; - struct zone *zone = NULL; - - if (pagevec_count(pvec) == 0) - return; - for (i = 0; i < pagevec_count(pvec); i++) { - struct page *page = pvec->pages[i]; - struct zone *pagezone = page_zone(page); - - if (pagezone != zone) { - if (zone) - spin_unlock_irq(&zone->lru_lock); - zone = pagezone; - spin_lock_irq(&zone->lru_lock); - } - if (!PageActive(page) && PageLRU(page)) - list_move(&page->lru, &pagezone->inactive_list); - } - if (zone) - spin_unlock_irq(&zone->lru_lock); - __pagevec_release(pvec); -} - /* * Add the passed pages to the LRU, then drop the caller's refcount * on them. Reinitialises the caller's pagevec. diff --git a/mm/vmscan.c b/mm/vmscan.c index b0cd7ff6265e..ae4a0ae1ab3c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -284,12 +284,6 @@ shrink_list(struct list_head *page_list, unsigned int gfp_mask, pte_chain_unlock(page); #endif /* CONFIG_SWAP */ - /* - * FIXME: this is CPU-inefficient for shared mappings. - * try_to_unmap() will set the page dirty and ->vm_writeback - * will write it. So we're back to page-at-a-time writepage - * in LRU order. - */ /* * If the page is dirty, only perform writeback if that write * will be non-blocking. To prevent this allocation from being @@ -308,13 +302,7 @@ shrink_list(struct list_head *page_list, unsigned int gfp_mask, * See swapfile.c:page_queue_congested(). */ if (PageDirty(page)) { - int (*writeback)(struct page *, - struct writeback_control *); struct backing_dev_info *bdi; - const int cluster_size = SWAP_CLUSTER_MAX; - struct writeback_control wbc = { - .nr_to_write = cluster_size, - }; if (!is_page_cache_freeable(page)) goto keep_locked; @@ -326,13 +314,15 @@ shrink_list(struct list_head *page_list, unsigned int gfp_mask, if (bdi != current->backing_dev_info && bdi_write_congested(bdi)) goto keep_locked; - - writeback = mapping->a_ops->vm_writeback; - if (writeback == NULL) - writeback = generic_vm_writeback; - (*writeback)(page, &wbc); - *max_scan -= (cluster_size - wbc.nr_to_write); - goto keep; + if (test_clear_page_dirty(page)) { + write_lock(&mapping->page_lock); + list_move(&page->list, &mapping->locked_pages); + write_unlock(&mapping->page_lock); + + if (mapping->a_ops->writepage(page) == -EAGAIN) + __set_page_dirty_nobuffers(page); + goto keep; + } } /* -- cgit v1.2.3 From 36fb7f8459cc42eca202f0ad7b2d051359406d57 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 21 Nov 2002 19:32:34 -0800 Subject: [PATCH] handle zones which are full of unreclaimable pages This patch is a general solution to the situation where a zone is full of pinned pages. This can come about if: a) Someone has allocated all of ZONE_DMA for IO buffers b) Some application is mlocking some memory and a zone ends up full of mlocked pages (can happen on a 1G ia32 system) c) All of ZONE_HIGHMEM is pinned in hugetlb pages (can happen on 1G machines) We'll currently burn 10% of CPU in kswapd when this happens, although it is quite hard to trigger. The algorithm is: - If page reclaim has scanned 2 * the total number of pages in the zone and there have been no pages freed in that zone then mark the zone as "all unreclaimable". - When a zone is "all unreclaimable" page reclaim almost ignores it. We will perform a "light" scan at DEF_PRIORITY (typically 1/4096'th of the zone, or 64 pages) and then forget about the zone. - When a batch of pages are freed into the zone, clear its "all unreclaimable" state and start full scanning again. The assumption being that some state change has come about which will make reclaim successful again. So if a "light scan" actually frees some pages, the zone will revert to normal state immediately. So we're effectively putting the zone into "low power" mode, and lightly polling it to see if something has changed. The code works OK, but is quite hard to test - I mainly tested it by pinning all highmem in hugetlb pages. --- include/linux/mmzone.h | 3 +++ mm/page_alloc.c | 16 ++++++++++------ mm/vmscan.c | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 3e004bc2ff63..f286bf9aeefd 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -84,6 +84,8 @@ struct zone { atomic_t refill_counter; unsigned long nr_active; unsigned long nr_inactive; + int all_unreclaimable; /* All pages pinned */ + unsigned long pages_scanned; /* since last reclaim */ ZONE_PADDING(_pad2_) @@ -203,6 +205,7 @@ memclass(struct zone *pgzone, struct zone *classzone) void get_zone_counts(unsigned long *active, unsigned long *inactive); void build_all_zonelists(void); +void wakeup_kswapd(struct zone *zone); /** * for_each_pgdat - helper macro to iterate over all nodes diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 16b70897ca42..d8921e02318a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -167,6 +167,12 @@ static inline void free_pages_check(const char *function, struct page *page) * Frees a list of pages. * Assumes all pages on list are in same zone, and of same order. * count is the number of pages to free, or 0 for all on the list. + * + * If the zone was previously in an "all pages pinned" state then look to + * see if this freeing clears that state. + * + * And clear the zone's pages_scanned counter, to hold off the "all pages are + * pinned" detection logic. */ static int free_pages_bulk(struct zone *zone, int count, @@ -181,6 +187,8 @@ free_pages_bulk(struct zone *zone, int count, base = zone->zone_mem_map; area = zone->free_area + order; spin_lock_irqsave(&zone->lock, flags); + zone->all_unreclaimable = 0; + zone->pages_scanned = 0; while (!list_empty(list) && count--) { page = list_entry(list->prev, struct page, list); /* have to delete it as __free_pages_bulk list manipulates */ @@ -464,12 +472,8 @@ __alloc_pages(unsigned int gfp_mask, unsigned int order, } /* we're somewhat low on memory, failed to find what we needed */ - for (i = 0; zones[i] != NULL; i++) { - struct zone *z = zones[i]; - if (z->free_pages <= z->pages_low && - waitqueue_active(&z->zone_pgdat->kswapd_wait)) - wake_up_interruptible(&z->zone_pgdat->kswapd_wait); - } + for (i = 0; zones[i] != NULL; i++) + wakeup_kswapd(zones[i]); /* Go through the zonelist again, taking __GFP_HIGH into account */ min = 1UL << order; diff --git a/mm/vmscan.c b/mm/vmscan.c index 42754033157b..42dfeadc6d9d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -468,6 +468,7 @@ shrink_cache(const int nr_pages, struct zone *zone, nr_taken++; } zone->nr_inactive -= nr_taken; + zone->pages_scanned += nr_taken; spin_unlock_irq(&zone->lru_lock); if (nr_taken == 0) @@ -720,6 +721,9 @@ shrink_zone(struct zone *zone, int max_scan, unsigned int gfp_mask, * satisfy the `incremental min' zone defense algorithm. * * Returns the number of reclaimed pages. + * + * If a zone is deemed to be full of pinned pages then just give it a light + * scan then give up on it. */ static int shrink_caches(struct zone *classzone, int priority, int *total_scanned, @@ -735,6 +739,9 @@ shrink_caches(struct zone *classzone, int priority, int *total_scanned, int nr_mapped = 0; int max_scan; + if (zone->all_unreclaimable && priority != DEF_PRIORITY) + continue; /* Let kswapd poll it */ + /* * If we cannot reclaim `nr_pages' pages by scanning twice * that many pages then fall back to the next zone. @@ -817,6 +824,14 @@ try_to_free_pages(struct zone *classzone, * special. * * Returns the number of pages which were actually freed. + * + * There is special handling here for zones which are full of pinned pages. + * This can happen if the pages are all mlocked, or if they are all used by + * device drivers (say, ZONE_DMA). Or if they are all in use by hugetlb. + * What we do is to detect the case where all pages in the zone have been + * scanned twice and there has been zero successful reclaim. Mark the zone as + * dead and from now on, only perform a short scan. Basically we're polling + * the zone for when the problem goes away. */ static int balance_pgdat(pg_data_t *pgdat, int nr_pages, struct page_state *ps) { @@ -833,6 +848,9 @@ static int balance_pgdat(pg_data_t *pgdat, int nr_pages, struct page_state *ps) int max_scan; int to_reclaim; + if (zone->all_unreclaimable && priority != DEF_PRIORITY) + continue; + if (nr_pages && to_free > 0) { /* Software suspend */ to_reclaim = min(to_free, SWAP_CLUSTER_MAX*8); } else { /* Zone balancing */ @@ -849,6 +867,10 @@ static int balance_pgdat(pg_data_t *pgdat, int nr_pages, struct page_state *ps) to_free -= shrink_zone(zone, max_scan, GFP_KSWAPD, to_reclaim, &nr_mapped, ps, priority); shrink_slab(max_scan + nr_mapped, GFP_KSWAPD); + if (zone->all_unreclaimable) + continue; + if (zone->pages_scanned > zone->present_pages * 2) + zone->all_unreclaimable = 1; } if (all_zones_ok) break; @@ -909,6 +931,18 @@ int kswapd(void *p) } } +/* + * A zone is low on free memory, so wake its kswapd task to service it. + */ +void wakeup_kswapd(struct zone *zone) +{ + if (zone->free_pages > zone->pages_low) + return; + if (!waitqueue_active(&zone->zone_pgdat->kswapd_wait)) + return; + wake_up_interruptible(&zone->zone_pgdat->kswapd_wait); +} + #ifdef CONFIG_SOFTWARE_SUSPEND /* * Try to free `nr_pages' of memory, system-wide. Returns the number of freed @@ -938,7 +972,6 @@ int shrink_all_memory(int nr_pages) static int __init kswapd_init(void) { pg_data_t *pgdat; - printk("Starting kswapd\n"); swap_setup(); for_each_pgdat(pgdat) kernel_thread(kswapd, pgdat, CLONE_KERNEL); -- cgit v1.2.3 From b1ad1f4efce23ad0801492c0d5ffa8c0aa6a8cdb Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 21 Nov 2002 19:32:45 -0800 Subject: [PATCH] no-buffer-head ext2 option Implements a new set of block address_space_operations which will never attach buffer_heads to file pagecache. These can be turned on for ext2 with the `nobh' mount option. During write-intensive testing on a 7G machine, total buffer_head storage remained below 0.3 megabytes. And those buffer_heads are against ZONE_NORMAL pagecache and will be reclaimed by ZONE_NORMAL memory pressure. This work is, of course, a special for the huge highmem machines. Possibly it obsoletes the buffer_heads_over_limit stuff (which doesn't work terribly well), but that code is simple, and will provide relief for other filesystems. It should be noted that the nobh_prepare_write() function and the PageMappedToDisk() infrastructure is what is needed to solve the problem of user data corruption when the filesystem which backs a sparse MAP_SHARED mapping runs out of space. We can use this code in filemap_nopage() to ensure that all mapped pages have space allocated on-disk. Deliver SIGBUS on ENOSPC. This will require a new address_space op, I expect. --- fs/buffer.c | 197 ++++++++++++++++++++++++++++++++++++++++++++ fs/ext2/ext2.h | 1 + fs/ext2/inode.c | 40 ++++++++- fs/ext2/namei.c | 15 +++- fs/ext2/super.c | 2 + fs/mpage.c | 4 + include/linux/buffer_head.h | 3 + include/linux/ext2_fs.h | 1 + include/linux/page-flags.h | 5 ++ mm/page_alloc.c | 2 +- mm/truncate.c | 1 + 11 files changed, 263 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/buffer.c b/fs/buffer.c index a0c7f850482f..aacd45b8b260 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1964,6 +1964,7 @@ int block_read_full_page(struct page *page, get_block_t *get_block) struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; unsigned int blocksize, blocks; int nr, i; + int fully_mapped = 1; if (!PageLocked(page)) PAGE_BUG(page); @@ -1986,6 +1987,7 @@ int block_read_full_page(struct page *page, get_block_t *get_block) continue; if (!buffer_mapped(bh)) { + fully_mapped = 0; if (iblock < lblock) { if (get_block(inode, iblock, bh, 0)) SetPageError(page); @@ -2008,6 +2010,9 @@ int block_read_full_page(struct page *page, get_block_t *get_block) arr[nr++] = bh; } while (i++, iblock++, (bh = bh->b_this_page) != head); + if (fully_mapped) + SetPageMappedToDisk(page); + if (!nr) { /* * All buffers are uptodate - we can set the page uptodate @@ -2204,6 +2209,198 @@ int generic_commit_write(struct file *file, struct page *page, return 0; } +/* + * On entry, the page is fully not uptodate. + * On exit the page is fully uptodate in the areas outside (from,to) + */ +int nobh_prepare_write(struct page *page, unsigned from, unsigned to, + get_block_t *get_block) +{ + struct inode *inode = page->mapping->host; + const unsigned blkbits = inode->i_blkbits; + const unsigned blocksize = 1 << blkbits; + struct buffer_head map_bh; + struct buffer_head *read_bh[MAX_BUF_PER_PAGE]; + unsigned block_in_page; + unsigned block_start; + sector_t block_in_file; + char *kaddr; + int nr_reads = 0; + int i; + int ret = 0; + int is_mapped_to_disk = 1; + int dirtied_it = 0; + + if (PageMappedToDisk(page)) + return 0; + + block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits); + map_bh.b_page = page; + + /* + * We loop across all blocks in the page, whether or not they are + * part of the affected region. This is so we can discover if the + * page is fully mapped-to-disk. + */ + for (block_start = 0, block_in_page = 0; + block_start < PAGE_CACHE_SIZE; + block_in_page++, block_start += blocksize) { + unsigned block_end = block_start + blocksize; + int create; + + map_bh.b_state = 0; + create = 1; + if (block_start >= to) + create = 0; + ret = get_block(inode, block_in_file + block_in_page, + &map_bh, create); + if (ret) + goto failed; + if (!buffer_mapped(&map_bh)) + is_mapped_to_disk = 0; + if (buffer_new(&map_bh)) + unmap_underlying_metadata(map_bh.b_bdev, + map_bh.b_blocknr); + if (PageUptodate(page)) + continue; + if (buffer_new(&map_bh) || !buffer_mapped(&map_bh)) { + kaddr = kmap_atomic(page, KM_USER0); + if (block_start < from) { + memset(kaddr+block_start, 0, from-block_start); + dirtied_it = 1; + } + if (block_end > to) { + memset(kaddr + to, 0, block_end - to); + dirtied_it = 1; + } + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + continue; + } + if (buffer_uptodate(&map_bh)) + continue; /* reiserfs does this */ + if (block_start < from || block_end > to) { + struct buffer_head *bh = alloc_buffer_head(); + + if (!bh) { + ret = -ENOMEM; + goto failed; + } + bh->b_state = map_bh.b_state; + atomic_set(&bh->b_count, 0); + bh->b_this_page = 0; + bh->b_page = page; + bh->b_blocknr = map_bh.b_blocknr; + bh->b_size = blocksize; + bh->b_data = (char *)block_start; + bh->b_bdev = map_bh.b_bdev; + bh->b_private = NULL; + read_bh[nr_reads++] = bh; + } + } + + if (nr_reads) { + ll_rw_block(READ, nr_reads, read_bh); + for (i = 0; i < nr_reads; i++) { + wait_on_buffer(read_bh[i]); + if (!buffer_uptodate(read_bh[i])) + ret = -EIO; + free_buffer_head(read_bh[i]); + read_bh[i] = NULL; + } + if (ret) + goto failed; + } + + if (is_mapped_to_disk) + SetPageMappedToDisk(page); + SetPageUptodate(page); + + /* + * Setting the page dirty here isn't necessary for the prepare_write + * function - commit_write will do that. But if/when this function is + * used within the pagefault handler to ensure that all mmapped pages + * have backing space in the filesystem, we will need to dirty the page + * if its contents were altered. + */ + if (dirtied_it) + set_page_dirty(page); + + return 0; + +failed: + for (i = 0; i < nr_reads; i++) { + if (read_bh[i]) + free_buffer_head(read_bh[i]); + } + + /* + * Error recovery is pretty slack. Clear the page and mark it dirty + * so we'll later zero out any blocks which _were_ allocated. + */ + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr, 0, PAGE_CACHE_SIZE); + kunmap_atomic(kaddr, KM_USER0); + SetPageUptodate(page); + set_page_dirty(page); + return ret; +} +EXPORT_SYMBOL(nobh_prepare_write); + +int nobh_commit_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + struct inode *inode = page->mapping->host; + loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + + set_page_dirty(page); + if (pos > inode->i_size) { + inode->i_size = pos; + mark_inode_dirty(inode); + } + return 0; +} +EXPORT_SYMBOL(nobh_commit_write); + +/* + * This function assumes that ->prepare_write() uses nobh_prepare_write(). + */ +int nobh_truncate_page(struct address_space *mapping, loff_t from) +{ + struct inode *inode = mapping->host; + unsigned blocksize = 1 << inode->i_blkbits; + pgoff_t index = from >> PAGE_CACHE_SHIFT; + unsigned offset = from & (PAGE_CACHE_SIZE-1); + unsigned to; + struct page *page; + struct address_space_operations *a_ops = mapping->a_ops; + char *kaddr; + int ret = 0; + + if ((offset & (blocksize - 1)) == 0) + goto out; + + ret = -ENOMEM; + page = grab_cache_page(mapping, index); + if (!page) + goto out; + + to = (offset + blocksize) & ~(blocksize - 1); + ret = a_ops->prepare_write(NULL, page, offset, to); + if (ret == 0) { + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + set_page_dirty(page); + } + unlock_page(page); + page_cache_release(page); +out: + return ret; +} +EXPORT_SYMBOL(nobh_truncate_page); + int block_truncate_page(struct address_space *mapping, loff_t from, get_block_t *get_block) { diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index e5de47ed3407..59b5921837cd 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -120,6 +120,7 @@ extern struct file_operations ext2_file_operations; /* inode.c */ extern struct address_space_operations ext2_aops; +extern struct address_space_operations ext2_nobh_aops; /* namei.c */ extern struct inode_operations ext2_dir_inode_operations; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 7fe83a044386..29cccde53b74 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -612,6 +612,13 @@ ext2_prepare_write(struct file *file, struct page *page, return block_prepare_write(page,from,to,ext2_get_block); } +static int +ext2_nobh_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + return nobh_prepare_write(page,from,to,ext2_get_block); +} + static sector_t ext2_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping,block,ext2_get_block); @@ -657,6 +664,18 @@ struct address_space_operations ext2_aops = { .writepages = ext2_writepages, }; +struct address_space_operations ext2_nobh_aops = { + .readpage = ext2_readpage, + .readpages = ext2_readpages, + .writepage = ext2_writepage, + .sync_page = block_sync_page, + .prepare_write = ext2_nobh_prepare_write, + .commit_write = nobh_commit_write, + .bmap = ext2_bmap, + .direct_IO = ext2_direct_IO, + .writepages = ext2_writepages, +}; + /* * Probably it should be a library function... search for first non-zero word * or memcmp with zero_page, whatever is better for particular architecture. @@ -864,7 +883,11 @@ void ext2_truncate (struct inode * inode) iblock = (inode->i_size + blocksize-1) >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); - block_truncate_page(inode->i_mapping, inode->i_size, ext2_get_block); + if (test_opt(inode->i_sb, NOBH)) + nobh_truncate_page(inode->i_mapping, inode->i_size); + else + block_truncate_page(inode->i_mapping, + inode->i_size, ext2_get_block); n = ext2_block_to_path(inode, iblock, offsets, NULL); if (n == 0) @@ -1044,17 +1067,26 @@ void ext2_read_inode (struct inode * inode) if (S_ISREG(inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; inode->i_fop = &ext2_file_operations; - inode->i_mapping->a_ops = &ext2_aops; + if (test_opt(inode->i_sb, NOBH)) + inode->i_mapping->a_ops = &ext2_nobh_aops; + else + inode->i_mapping->a_ops = &ext2_aops; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ext2_dir_inode_operations; inode->i_fop = &ext2_dir_operations; - inode->i_mapping->a_ops = &ext2_aops; + if (test_opt(inode->i_sb, NOBH)) + inode->i_mapping->a_ops = &ext2_nobh_aops; + else + inode->i_mapping->a_ops = &ext2_aops; } else if (S_ISLNK(inode->i_mode)) { if (ext2_inode_is_fast_symlink(inode)) inode->i_op = &ext2_fast_symlink_inode_operations; else { inode->i_op = &ext2_symlink_inode_operations; - inode->i_mapping->a_ops = &ext2_aops; + if (test_opt(inode->i_sb, NOBH)) + inode->i_mapping->a_ops = &ext2_nobh_aops; + else + inode->i_mapping->a_ops = &ext2_aops; } } else { inode->i_op = &ext2_special_inode_operations; diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 9f0788e3f6ef..04489df5a2e5 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -127,7 +127,10 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode) if (!IS_ERR(inode)) { inode->i_op = &ext2_file_inode_operations; inode->i_fop = &ext2_file_operations; - inode->i_mapping->a_ops = &ext2_aops; + if (test_opt(inode->i_sb, NOBH)) + inode->i_mapping->a_ops = &ext2_nobh_aops; + else + inode->i_mapping->a_ops = &ext2_aops; mark_inode_dirty(inode); err = ext2_add_nondir(dentry, inode); } @@ -168,7 +171,10 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry, if (l > sizeof (EXT2_I(inode)->i_data)) { /* slow symlink */ inode->i_op = &ext2_symlink_inode_operations; - inode->i_mapping->a_ops = &ext2_aops; + if (test_opt(inode->i_sb, NOBH)) + inode->i_mapping->a_ops = &ext2_nobh_aops; + else + inode->i_mapping->a_ops = &ext2_aops; err = page_symlink(inode, symname, l); if (err) goto out_fail; @@ -222,7 +228,10 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode->i_op = &ext2_dir_inode_operations; inode->i_fop = &ext2_dir_operations; - inode->i_mapping->a_ops = &ext2_aops; + if (test_opt(inode->i_sb, NOBH)) + inode->i_mapping->a_ops = &ext2_nobh_aops; + else + inode->i_mapping->a_ops = &ext2_aops; ext2_inc_count(inode); diff --git a/fs/ext2/super.c b/fs/ext2/super.c index a0acf56bd781..23116c16e3ce 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -391,6 +391,8 @@ static int parse_options (char * options, set_opt (sbi->s_mount_opt, OLDALLOC); else if (!strcmp (this_char, "orlov")) clear_opt (sbi->s_mount_opt, OLDALLOC); + else if (!strcmp (this_char, "nobh")) + set_opt(sbi->s_mount_opt, NOBH); /* Silently ignore the quota options */ else if (!strcmp (this_char, "grpquota") || !strcmp (this_char, "noquota") diff --git a/fs/mpage.c b/fs/mpage.c index 05622a8e515a..8307f43f18b6 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -178,6 +178,7 @@ do_mpage_readpage(struct bio *bio, struct page *page, unsigned nr_pages, struct block_device *bdev = NULL; struct buffer_head bh; int length; + int fully_mapped = 1; if (page_has_buffers(page)) goto confused; @@ -194,6 +195,7 @@ do_mpage_readpage(struct bio *bio, struct page *page, unsigned nr_pages, } if (!buffer_mapped(&bh)) { + fully_mapped = 0; if (first_hole == blocks_per_page) first_hole = page_block; continue; @@ -220,6 +222,8 @@ do_mpage_readpage(struct bio *bio, struct page *page, unsigned nr_pages, unlock_page(page); goto out; } + } else if (fully_mapped) { + SetPageMappedToDisk(page); } /* diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 8587dd6f7146..4e7a9bbf99dd 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -190,6 +190,9 @@ 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 *); int file_fsync(struct file *, struct dentry *, int); +int nobh_prepare_write(struct page*, unsigned, unsigned, get_block_t*); +int nobh_commit_write(struct file *, struct page *, unsigned, unsigned); +int nobh_truncate_page(struct address_space *, loff_t); #define OSYNC_METADATA (1<<0) #define OSYNC_DATA (1<<1) diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 0d008cfb99a8..d701ba88c688 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -308,6 +308,7 @@ struct ext2_inode { #define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ #define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ #define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NOBH 0x0100 /* No buffer_heads */ #define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ #define EXT2_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ #define EXT2_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */ diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 44480d80952f..f02449871c07 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -70,6 +70,7 @@ #define PG_chainlock 15 /* lock bit for ->pte_chain */ #define PG_direct 16 /* ->pte_chain points directly at pte */ +#define PG_mappedtodisk 17 /* Has blocks allocated on-disk */ /* * Global page accounting. One instance per CPU. Only unsigned longs are @@ -233,6 +234,10 @@ extern void get_full_page_state(struct page_state *ret); #define ClearPageDirect(page) clear_bit(PG_direct, &(page)->flags) #define TestClearPageDirect(page) test_and_clear_bit(PG_direct, &(page)->flags) +#define PageMappedToDisk(page) test_bit(PG_mappedtodisk, &(page)->flags) +#define SetPageMappedToDisk(page) set_bit(PG_mappedtodisk, &(page)->flags) +#define ClearPageMappedToDisk(page) clear_bit(PG_mappedtodisk, &(page)->flags) + /* * The PageSwapCache predicate doesn't use a PG_flag at this time, * but it may again do so one day. diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d8921e02318a..a810142b49c0 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -265,7 +265,7 @@ static void prep_new_page(struct page *page, int order) page->flags &= ~(1 << PG_uptodate | 1 << PG_error | 1 << PG_referenced | 1 << PG_arch_1 | - 1 << PG_checked); + 1 << PG_checked | 1 << PG_mappedtodisk); set_page_refs(page, order); } diff --git a/mm/truncate.c b/mm/truncate.c index 884b4e3930c2..25843367c31e 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -52,6 +52,7 @@ truncate_complete_page(struct address_space *mapping, struct page *page) clear_page_dirty(page); ClearPageUptodate(page); + ClearPageMappedToDisk(page); remove_from_page_cache(page); page_cache_release(page); /* pagecache ref */ } -- cgit v1.2.3