This is a large patch containing the in-kernel GSSD client. This
allows the kernel to communicate (via a loopback RPC) with a userspace
daemon which provides translation between named principals and uid/gid's.
The routines gss_get_name(), gss_get_num() are the only interface to
the GSSD client, and allow translation of a single id->name, or name->id,
respectively. The results of these routines are cached by the GSSD
client, so that subsequent calls do not require an expensive upcall
to userspace.
--- old/include/linux/sunrpc/clnt.h Wed Jul 24 16:03:29 2002
+++ new/include/linux/sunrpc/clnt.h Mon Jul 29 11:50:09 2002
@@ -149,5 +149,29 @@ extern void rpciod_wake_up(void);
*/
int rpc_getport_external(struct sockaddr_in *, __u32, __u32, int);
+#ifdef CONFIG_SUNRPC_GSSD_CLNT
+/*
+ * RPCSEC_GSS mapping between uid/gid's and named principals
+ */
+#define GSS_OWNER 0
+#define GSS_GROUP 1
+
+struct gss_cacheent {
+ uid_t id;
+ struct qstr name;
+
+ /* fields after this point are private, for use by the gss cache */
+ atomic_t refcount;
+ struct list_head name_hash;
+ struct list_head id_hash;
+};
+
+extern void gss_put(struct gss_cacheent *p);
+extern int gss_get_name(int type, uid_t id, struct gss_cacheent **pp);
+extern int gss_get_num(int type, unsigned int len, const unsigned char *name, uid_t *pp);
+extern void gss_cache_init(void);
+extern void gss_cache_exit(void);
+#endif /* CONFIG_SUNRPC_GSSD_CLNT */
+
#endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_CLNT_H */
--- old/net/sunrpc/stats.c Wed Jul 24 16:03:32 2002
+++ new/net/sunrpc/stats.c Mon Jul 29 11:50:09 2002
@@ -187,6 +187,9 @@ rpc_proc_exit(void)
static int __init
init_sunrpc(void)
{
+#ifdef CONFIG_SUNRPC_GSSD_CLNT
+ gss_cache_init();
+#endif
#ifdef RPC_DEBUG
rpc_register_sysctl();
#endif
@@ -201,6 +204,9 @@ cleanup_sunrpc(void)
rpc_unregister_sysctl();
#endif
rpc_proc_exit();
+#ifdef CONFIG_SUNRPC_GSSD_CLNT
+ gss_cache_exit();
+#endif
}
MODULE_LICENSE("GPL");
module_init(init_sunrpc);
--- old/net/sunrpc/sunrpc_syms.c Wed Jul 24 16:03:31 2002
+++ new/net/sunrpc/sunrpc_syms.c Mon Jul 29 11:50:09 2002
@@ -100,6 +100,13 @@ EXPORT_SYMBOL(xdr_encode_pages);
EXPORT_SYMBOL(xdr_inline_pages);
EXPORT_SYMBOL(xdr_shift_buf);
+/* RPCSEC_GSS name to uid mapping */
+#ifdef CONFIG_SUNRPC_GSSD_CLNT
+EXPORT_SYMBOL(gss_put);
+EXPORT_SYMBOL(gss_get_name);
+EXPORT_SYMBOL(gss_get_num);
+#endif
+
/* Debugging symbols */
#ifdef RPC_DEBUG
EXPORT_SYMBOL(rpc_debug);
--- old/net/sunrpc/gssd_clnt.c Thu Aug 8 10:42:56 2002
+++ new/net/sunrpc/gssd_clnt.c Tue Aug 6 08:23:46 2002
@@ -0,0 +1,580 @@
+/*
+ * net/sunrpc/gssd_clnt.c
+ *
+ * GSSD client.
+ *
+ * Copyright (c) 2000 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Dug Song <[email protected]>
+ * Andy Adamson <[email protected]>
+ * Kendrick Smith <[email protected]>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/uio.h>
+#include <linux/in.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/sched.h>
+
+typedef struct xdr_netobj GSS_BUFFER_T;
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_AUTH
+#endif
+
+/*
+ * GSSD gss.x definitions
+ */
+
+#define RPC_GSSD_PROGRAM 100666
+#define RPC_GSSD_VERSION 1
+
+enum gssd_proc {
+ GSSD_NAME_TO_UID = 1,
+ GSSD_UID_TO_NAME = 2,
+ GSSD_NAME_TO_GID = 3,
+ GSSD_GID_TO_NAME = 4
+};
+
+struct rpc_program gssd_program;
+
+static struct rpc_clnt *
+gssd_create(void)
+{
+ struct sockaddr_in sin;
+ struct rpc_xprt *xprt;
+ struct rpc_clnt *clnt;
+ uint saved_fsuid = current->fsuid;
+ kernel_cap_t saved_cap = current->cap_effective;
+
+ dprintk("RPC: gssd_create()\n");
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = 0;
+
+ /* create rpc socket as root so we get a priv port */
+ current->fsuid = 0;
+ cap_raise(current->cap_effective, CAP_NET_BIND_SERVICE);
+ xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL);
+ current->fsuid = saved_fsuid;
+ current->cap_effective = saved_cap;
+ if(xprt == NULL)
+ return NULL;
+
+ clnt = rpc_create_client(xprt, "localhost", &gssd_program,
+ RPC_GSSD_VERSION, RPC_AUTH_UNIX);
+ if (!clnt) {
+ xprt_destroy(xprt);
+ } else {
+ clnt->cl_softrtry = 1;
+ clnt->cl_chatty = 1;
+ clnt->cl_oneshot = 1;
+ clnt->cl_autobind = 1;
+ }
+ return clnt;
+}
+
+/*
+ * Convert Unix/GSS username/principal to Unix UID.
+ */
+static int
+gssd_name_to_uid(struct qstr *name, u32 *uidp)
+{
+ struct rpc_clnt *gssd_clnt;
+ unsigned int error = 0;
+
+ dprintk("RPC: gssd_name_to_uid \n");
+ if (!(gssd_clnt = gssd_create())) {
+ printk("RPC: couldn't create gssd client\n");
+ return -EACCES;
+ }
+
+ error = rpc_call(gssd_clnt, GSSD_NAME_TO_UID, name, uidp, RPC_TASK_ROOTCREDS);
+
+ if (error < 0) {
+ printk(KERN_WARNING
+ "RPC: failed to contact gssd (errno %d).\n", error);
+ }
+ dprintk("RPC: gssd_name_to_uid(%.*s) = %d\n",
+ (int) name->len, name->name, *uidp);
+
+ /* Client deleted automatically because cl_oneshot == 1 */
+ return error;
+}
+
+/*
+ * Convert Unix UID to Unix username.
+ */
+static int
+gssd_uid_to_name(u32 *uid, struct qstr *name)
+{
+ struct rpc_clnt *gssd_clnt;
+ unsigned int error = 0;
+
+ if (!(gssd_clnt = gssd_create())) {
+ printk("RPC: couldn't create gssd client\n");
+ return -EACCES;
+ }
+ error = rpc_call(gssd_clnt, GSSD_UID_TO_NAME, uid, name, RPC_TASK_ROOTCREDS);
+
+ if (error < 0) {
+ printk(KERN_WARNING
+ "RPC: failed to contact gssd (errno %d).\n", error);
+ }
+ dprintk("RPC: gssd_uid_to_name(%d) = %.*s\n",
+ *uid, (int) name->len, name->name);
+
+ /* Client deleted automatically because cl_oneshot == 1 */
+ return error;
+}
+
+/*
+ * Convert Unix group name to Unix GID.
+ */
+static int
+gssd_name_to_gid(struct qstr *name, u32 *gidp)
+{
+ struct rpc_clnt *gssd_clnt;
+ unsigned int error = 0;
+
+ if (!(gssd_clnt = gssd_create())) {
+ printk("RPC: couldn't create gssd client\n");
+ return -EACCES;
+ }
+ error = rpc_call(gssd_clnt, GSSD_NAME_TO_GID, name, gidp, RPC_TASK_ROOTCREDS);
+
+ if (error < 0) {
+ printk(KERN_WARNING
+ "RPC: failed to contact gssd (errno %d).\n", error);
+ }
+ dprintk("RPC: gssd_name_to_gid(%.*s) = %d\n",
+ (int) name->len, name->name, *gidp);
+
+ /* Client deleted automatically because cl_oneshot == 1 */
+ return error;
+}
+
+/*
+ * Convert Unix GID to Unix name.
+ */
+static int
+gssd_gid_to_name(u32 *gid, struct qstr *name)
+{
+ struct rpc_clnt *gssd_clnt;
+ unsigned int error = 0;
+
+ if (!(gssd_clnt = gssd_create())) {
+ printk("RPC: couldn't create gssd client\n");
+ return -EACCES;
+ }
+ error = rpc_call(gssd_clnt, GSSD_GID_TO_NAME, gid, name, RPC_TASK_ROOTCREDS);
+
+ if (error < 0) {
+ printk(KERN_WARNING
+ "RPC: failed to contact gssd (errno %d).\n", error);
+ }
+ dprintk("RPC: gssd_gid_to_name(%d) = %.*s\n",
+ *gid, (int) name->len, name->name);
+
+ /* Client deleted automatically because cl_oneshot == 1 */
+ return error;
+}
+
+/*
+ * XDR encode/decode functions for GSSD
+ */
+static int
+xdr_error(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ return -EIO;
+}
+
+static int
+xdr_decode_name(struct rpc_rqst *req, u32 *p, struct qstr *name)
+{
+ unsigned int len;
+
+ len = ntohl(*p++);
+ if (len > XDR_MAX_NETOBJ)
+ return -EIO;
+ if (!(name->name = kmalloc(len, GFP_KERNEL)))
+ return -ENOMEM;
+ name->len = len;
+ memcpy((char *)name->name, p, len); /* cast is to get rid of "const" */
+
+ dprintk("RPC: xdr_decode_name() = (%.*s)\n", (int)name->len, name->name);
+ return 0;
+}
+
+static int
+xdr_encode_name(struct rpc_rqst *req, u32 *p, struct qstr *name)
+{
+ unsigned int quadlen = XDR_QUADLEN(name->len);
+
+ dprintk("RPC: xdr_encode_name(%.*s)\n", (int) name->len, name->name);
+
+ if (name->len > XDR_MAX_NETOBJ)
+ return -EINVAL;
+ p[quadlen] = 0; /* zero trailing bytes */
+ *p++ = htonl(name->len);
+ memcpy(p, name->name, name->len);
+ p += quadlen;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+xdr_decode_id(struct rpc_rqst *req, u32 *p, u32 *idp)
+{
+ *idp = (u32) ntohl(*p);
+ dprintk("RPC: xdr_decode_id() = (%d)\n", *idp);
+ return 0;
+}
+
+static int
+xdr_encode_id(struct rpc_rqst *req, u32 *p, u32 *idp)
+{
+ dprintk("RPC: xdr_encode_id(%d)\n", *idp);
+ *p++ = (u32) htonl(*idp);
+
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static struct rpc_procinfo gssd_procedures[] = {
+ { "gssd_null",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "gssd_name_to_uid",
+ (kxdrproc_t) xdr_encode_name,
+ (kxdrproc_t) xdr_decode_id, RPC_MAXNETNAMELEN >> 2, 1 },
+ { "gssd_uid_to_name",
+ (kxdrproc_t) xdr_encode_id,
+ (kxdrproc_t) xdr_decode_name, RPC_MAXNETNAMELEN >> 2, 1 },
+ { "gssd_name_to_gid",
+ (kxdrproc_t) xdr_encode_name,
+ (kxdrproc_t) xdr_decode_id, RPC_MAXNETNAMELEN >> 2, 1 },
+ { "gssd_gid_to_name",
+ (kxdrproc_t) xdr_encode_id,
+ (kxdrproc_t) xdr_decode_name, RPC_MAXNETNAMELEN >> 2, 1 }
+};
+
+static struct rpc_version gssd_version1 = {
+ 1,
+ sizeof(gssd_procedures) / sizeof(gssd_procedures[0]),
+ gssd_procedures
+};
+
+static struct rpc_version * gssd_version[] = {
+ NULL,
+ &gssd_version1
+};
+
+static struct rpc_stat gssd_stats;
+
+struct rpc_program gssd_program = {
+ "GSSD",
+ RPC_GSSD_PROGRAM,
+ sizeof(gssd_version) / sizeof(gssd_version[0]),
+ gssd_version,
+ &gssd_stats,
+};
+
+/*
+ * The rest of this file is concerned with a cache for
+ * name<->id conversion, so that we don't have to pay the
+ * cost of an RPC upcall every time. There are a couple
+ * of outstanding issues here:
+ *
+ * TODO: Implement a mechanism for flushing infrequently-used
+ * entries from the cache. As things currently stand, a
+ * cache entry is permanent(!) once added. This might be
+ * as simple as imposing a hard limit on the number of
+ * cache entries. Alternatively, we might try to let kswapd
+ * free some entries when it begins to feel memory pressure.
+ *
+ * TODO: When gssd is restarted, we want to inform the kernel
+ * that the cache should be cleared. We need to create a hook
+ * somewhere (probably in /proc) that will let a userspace
+ * program clear the cache, and to modify gssd so that it will
+ * clear the cache on startup.
+ */
+
+#define GSS_HASH_BITS 8
+#define GSS_HASH_SIZE (1 << GSS_HASH_BITS)
+#define GSS_HASH_MASK (GSS_HASH_SIZE - 1)
+
+#define GSS_ID_HASH(id) ((id) & GSS_HASH_MASK)
+#define GSS_NAME_HASH(name, len) (full_name_hash((name), (len)) & GSS_HASH_MASK)
+
+struct gss_mapping {
+ rwlock_t lock;
+ struct list_head id_hash[GSS_HASH_SIZE];
+ struct list_head name_hash[GSS_HASH_SIZE];
+};
+
+static struct gss_mapping owner_map;
+static struct gss_mapping group_map;
+
+static void
+gss_mapping_init(struct gss_mapping *map)
+{
+ int i;
+
+ rwlock_init(&map->lock);
+ for (i = 0; i < GSS_HASH_SIZE; i++) {
+ INIT_LIST_HEAD(&map->id_hash[i]);
+ INIT_LIST_HEAD(&map->name_hash[i]);
+ }
+}
+
+static void
+gss_mapping_destroy(struct gss_mapping *map)
+{
+ int i;
+ struct list_head *l;
+ struct gss_cacheent *p;
+
+ for (i = 0; i < GSS_HASH_SIZE; i++) {
+ l = &map->id_hash[i];
+ while (!list_empty(l)) {
+ p = list_entry(l->next, struct gss_cacheent, id_hash);
+ list_del_init(&p->id_hash);
+ list_del_init(&p->name_hash);
+ gss_put(p);
+ }
+ }
+}
+
+/*
+ * I didn't bother putting these in a slab cache, since whenever
+ * one is allocated, we already incur the overhead of an RPC call
+ * to userspace...
+ */
+static struct gss_cacheent *
+gss_alloc(unsigned int namelen)
+{
+ struct gss_cacheent *p;
+
+ BUG_ON(namelen > XDR_MAX_NETOBJ);
+
+ if (!(p = kmalloc(sizeof(*p), GFP_KERNEL)))
+ return NULL;
+ atomic_set(&p->refcount, 1);
+ INIT_LIST_HEAD(&p->name_hash);
+ INIT_LIST_HEAD(&p->id_hash);
+
+ if (!namelen) {
+ p->name.name = NULL;
+ return p;
+ }
+ if (!(p->name.name = kmalloc(namelen, GFP_KERNEL))) {
+ kfree(p);
+ return NULL;
+ }
+ return p;
+}
+
+void
+gss_put(struct gss_cacheent *p)
+{
+ BUG_ON(atomic_read(&p->refcount) == 0);
+
+ if (!atomic_dec_and_test(&p->refcount))
+ return;
+ BUG_ON(!list_empty(&p->name_hash) || !list_empty(&p->id_hash));
+
+ if (p->name.name) {
+ kfree((char *)p->name.name); /* cast is to get rid of "const" */
+ p->name.name = NULL;
+ }
+ kfree(p);
+
+}
+
+/* caller must hold writelock */
+static inline void
+gss_insert(struct gss_cacheent *p, struct gss_mapping *map)
+{
+ unsigned int id_hashval = GSS_ID_HASH(p->id);
+ unsigned int name_hashval = p->name.hash;
+
+ list_add(&p->id_hash, &map->id_hash[id_hashval]);
+ list_add(&p->name_hash, &map->name_hash[name_hashval]);
+}
+
+/* caller must hold readlock */
+static inline struct gss_cacheent *
+gss_searchbyid(struct gss_mapping *map, uid_t id)
+{
+ unsigned int hashval = GSS_ID_HASH(id);
+ struct list_head *l;
+ struct gss_cacheent *p;
+
+ list_for_each(l, &map->id_hash[hashval]) {
+ p = list_entry(l, struct gss_cacheent, id_hash);
+ if (p->id == id)
+ return p;
+ }
+ return NULL;
+}
+
+/* caller must hold readlock */
+static inline struct gss_cacheent *
+gss_searchbyname(struct gss_mapping *map, unsigned int len, const unsigned char *name,
+ unsigned int hash)
+{
+ unsigned int hashval = hash & GSS_HASH_MASK;
+ struct list_head *l;
+ struct gss_cacheent *p;
+
+ list_for_each(l, &map->name_hash[hashval]) {
+ p = list_entry(l, struct gss_cacheent, name_hash);
+ if ((p->name.len == len) && !memcmp(p->name.name, name, len))
+ return p;
+ }
+ return NULL;
+}
+
+int
+gss_get_name(int type, uid_t id, struct gss_cacheent **pp)
+{
+ struct gss_mapping *map;
+ struct gss_cacheent *p, *q;
+ int status;
+
+ switch (type) {
+ case GSS_OWNER:
+ map = &owner_map;
+ break;
+ case GSS_GROUP:
+ map = &group_map;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ read_lock(&map->lock);
+ if ((p = gss_searchbyid(map, id))) {
+ atomic_inc(&p->refcount);
+ read_unlock(&map->lock);
+ *pp = p;
+ return 0;
+ }
+ read_unlock(&map->lock);
+
+ if (!(p = gss_alloc(0)))
+ return -ENOMEM;
+ p->id = id;
+
+ if (type == GSS_OWNER)
+ status = gssd_uid_to_name(&id, &p->name);
+ else /* GSS_GROUP */
+ status = gssd_gid_to_name(&id, &p->name);
+ if (status < 0) {
+ gss_put(p);
+ return status;
+ }
+ p->name.hash = GSS_NAME_HASH(p->name.name, p->name.len);
+
+ write_lock(&map->lock);
+ if ((q = gss_searchbyid(map, id))) { /* retry the search, since we slept */
+ *pp = q;
+ atomic_inc(&q->refcount);
+ write_unlock(&map->lock);
+ gss_put(p);
+ return 0;
+ }
+ gss_insert(p, map);
+ atomic_inc(&p->refcount);
+ write_unlock(&map->lock);
+ *pp = p;
+ return 0;
+}
+
+int
+gss_get_num(int type, unsigned int len, const unsigned char *name, uid_t *pp)
+{
+ unsigned int hash;
+ struct gss_mapping *map;
+ struct gss_cacheent *p, *q;
+ int status;
+
+ if (len > XDR_MAX_NETOBJ)
+ return -EINVAL;
+
+ hash = GSS_NAME_HASH(name, len);
+
+ switch (type) {
+ case GSS_OWNER:
+ map = &owner_map;
+ break;
+ case GSS_GROUP:
+ map = &group_map;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ read_lock(&map->lock);
+ if ((p = gss_searchbyname(map, len, name, hash))) {
+ read_unlock(&map->lock);
+ *pp = p->id;
+ return 0;
+ }
+ read_unlock(&map->lock);
+
+ if (!(p = gss_alloc(len)))
+ return -ENOMEM;
+ p->name.len = len;
+ memcpy((char *)p->name.name, name, len); /* cast is to get rid of "const" */
+ p->name.hash = hash;
+
+ if (type == GSS_OWNER)
+ status = gssd_name_to_uid(&p->name, &p->id);
+ else /* GSS_GROUP */
+ status = gssd_name_to_gid(&p->name, &p->id);
+ if (status < 0) {
+ gss_put(p);
+ return status;
+ }
+
+ write_lock(&map->lock);
+ if ((q = gss_searchbyname(map, len, name, hash))) {
+ *pp = q->id;
+ write_unlock(&map->lock);
+ gss_put(p);
+ return 0;
+ }
+ *pp = p->id;
+ gss_insert(p, map);
+ write_unlock(&map->lock);
+ return 0;
+}
+
+void
+gss_cache_init(void)
+{
+ gss_mapping_init(&owner_map);
+ gss_mapping_init(&group_map);
+}
+
+void
+gss_cache_exit(void)
+{
+ gss_mapping_destroy(&group_map);
+ gss_mapping_destroy(&owner_map);
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
--- old/net/sunrpc/Makefile Wed Jul 24 16:03:19 2002
+++ new/net/sunrpc/Makefile Mon Jul 29 11:50:09 2002
@@ -13,6 +13,8 @@ sunrpc-y := clnt.o xprt.o sched.o \
sunrpc_syms.o
sunrpc-$(CONFIG_PROC_FS) += stats.o
sunrpc-$(CONFIG_SYSCTL) += sysctl.o
+sunrpc-$(CONFIG_SUNRPC_GSSD_CLNT) += gssd_clnt.o
+
sunrpc-objs := $(sunrpc-y)
include $(TOPDIR)/Rules.make
On Wed, 2002-08-14 at 00:22, Imran Badr wrote:
> How can I define a certain region of memory so that it is never cached? I
> want to use non-cached region of memory to communicate to my PCI device to
> avoid system overhead in cache snooping.
Architecture specific and for RAM quite often not supported at all. You
can use ioremap_nocache on ram in theory but that doesn't actually work
on most processors, will MCE on the PPro and needs some work on Athlon
XP/MP
K6/K5 don't support it at all from memory.
If your hardware is sane it will already be doing cache line size bursts
and MWI.
On Tue, Aug 13, 2002 at 04:22:31PM -0700, Imran Badr wrote:
> How can I define a certain region of memory so that it is never cached? I
> want to use non-cached region of memory to communicate to my PCI device to
> avoid system overhead in cache snooping.
On every sane platform the hardware performs better at keeping the
coherency than software ever could. Don't even think about it unless
you for some reason absolutely must disable caching. Aside and as Alan
already mentioned the allocation of uncached memory isn't supported.
Ralf
Hi,
How can I define a certain region of memory so that it is never cached? I
want to use non-cached region of memory to communicate to my PCI device to
avoid system overhead in cache snooping.
Thanks,
Imran.
On Tue, Aug 13, 2002 at 05:19:56PM -0700, Imran Badr wrote:
> What if I allocate memory at boot-time using alloc_bootmem*(..)?
That wouldn't change the picture either.
Ralf
Thanks Ralf and Alan,
What if I allocate memory at boot-time using alloc_bootmem*(..)?
Imran.
-----Original Message-----
From: Ralf Baechle [mailto:[email protected]]
Sent: Tuesday, August 13, 2002 5:08 PM
To: Imran Badr
Cc: [email protected]
Subject: Re: Cache coherency and snooping
On Tue, Aug 13, 2002 at 04:22:31PM -0700, Imran Badr wrote:
> How can I define a certain region of memory so that it is never cached? I
> want to use non-cached region of memory to communicate to my PCI device to
> avoid system overhead in cache snooping.
On every sane platform the hardware performs better at keeping the
coherency than software ever could. Don't even think about it unless
you for some reason absolutely must disable caching. Aside and as Alan
already mentioned the allocation of uncached memory isn't supported.
Ralf
Hi,
Please advise if following sequence of operations are going to help:
alloc memory
reserve the page
flush every cache
call ioremap_nocache
Thanks,
Imran.
-----Original Message-----
From: Ralf Baechle [mailto:[email protected]]
Sent: Tuesday, August 13, 2002 5:30 PM
To: Imran Badr
Cc: 'Alan Cox'; [email protected]
Subject: Re: Cache coherency and snooping
On Tue, Aug 13, 2002 at 05:19:56PM -0700, Imran Badr wrote:
> What if I allocate memory at boot-time using alloc_bootmem*(..)?
That wouldn't change the picture either.
Ralf
On Tue, 13 Aug 2002, Imran Badr wrote:
> Please advise if following sequence of operations are going to help:
>
> alloc memory
> reserve the page
> flush every cache
> call ioremap_nocache
Won't work around hardware limitations. If the hardware
cannot turn off caching, all you could do is flush the
page to ram before every explicit IO request...
regards,
Rik
--
Bravely reimplemented by the knights who say "NIH".
http://www.surriel.com/ http://distro.conectiva.com/
I am looking at the linux kernel code. The driver in /drivers/char/agp uses
this same seqeuence to allocate non-cacheable memory page ( look at the
function static int agp_generic_create_gatt_table(void)).
I will give it a try on my mips based platform and let you guys know the
outcome.
Thanks to everyone for all the responses and guidance.
Imran.
-----Original Message-----
From: Rik van Riel [mailto:[email protected]]
Sent: Tuesday, August 13, 2002 6:38 PM
To: Imran Badr
Cc: 'Ralf Baechle'; 'Alan Cox'; [email protected]
Subject: RE: Cache coherency and snooping
On Tue, 13 Aug 2002, Imran Badr wrote:
> Please advise if following sequence of operations are going to help:
>
> alloc memory
> reserve the page
> flush every cache
> call ioremap_nocache
Won't work around hardware limitations. If the hardware
cannot turn off caching, all you could do is flush the
page to ram before every explicit IO request...
regards,
Rik
--
Bravely reimplemented by the knights who say "NIH".
http://www.surriel.com/ http://distro.conectiva.com/
On Tue, Aug 13, 2002 at 06:00:43PM -0700, Imran Badr wrote:
> Please advise if following sequence of operations are going to help:
>
> alloc memory
> reserve the page
> flush every cache
That's an extremly expensive operation on some platforms and there's no
portable kernel API to do it.
Note that the flush_cache_*() functions are not suitable for I/O as you
want to do it. Many platforms such as i386 implement these functions
as empty functions.
> call ioremap_nocache
You said you intend to remap memory, that is RAM. The ioremap*() functions
will refuse to remap RAM; they're only suitable for other types of memory
such as PCI boards. If the latter was your intension then ioremap_nocache()
will work as intended.
Again, unless your hardware is fucked beyond recognition it'll do a better
job at keeping cache coherence than software.
Ralf
From: Ralf Baechle <[email protected]>
Date: Wed, 14 Aug 2002 10:16:54 +0200
If the latter was your intension then ioremap_nocache()
will work as intended.
There are platforms btw where non-L2-cacheable access to RAM is
impossible. It will hang the system in fact if one asked the cpu to
do it.