summaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-06-19 22:45:37 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-06-19 22:45:37 +0000
commit6d403070f28cd44860fdb3a53be5da0275c65cf4 (patch)
tree0d0e7fe7b5fb7568d19e11d7d862b77a866ce081 /ipc
parentecf1bf5f6c2e668d03b0a9fb026db7aa41e292e1 (diff)
Merge with 2.4.0-test1-ac21 + pile of MIPS cleanups to make merging
possible. Chainsawed RM200 kernel to compile again. Jazz machine status unknown.
Diffstat (limited to 'ipc')
-rw-r--r--ipc/shm.c128
-rw-r--r--ipc/util.c106
2 files changed, 199 insertions, 35 deletions
diff --git a/ipc/shm.c b/ipc/shm.c
index d6aaf0311..7a255f85a 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -16,11 +16,9 @@
*
* The filesystem has the following restrictions/bugs:
* 1) It only can handle one directory.
- * 2) Because the directory is represented by the SYSV shm array it
- * can only be mounted one time.
- * 3) Private writeable mappings are not supported
- * 4) Read and write are not implemented (should they?)
- * 5) No special nodes are supported
+ * 2) Private writeable mappings are not supported
+ * 3) Read and write are not implemented (should they?)
+ * 4) No special nodes are supported
*
* There are the following mount options:
* - nr_blocks (^= shmall) is the number of blocks of size PAGE_SIZE
@@ -54,7 +52,7 @@ static struct super_block *shm_read_super(struct super_block *,void *, int);
static void shm_put_super (struct super_block *);
static int shm_remount_fs (struct super_block *, int *, char *);
static void shm_read_inode (struct inode *);
-static void shm_write_inode(struct inode *);
+static void shm_write_inode(struct inode *, int);
static int shm_statfs (struct super_block *, struct statfs *);
static int shm_create (struct inode *,struct dentry *,int);
static struct dentry *shm_lookup (struct inode *,struct dentry *);
@@ -335,7 +333,7 @@ static inline struct shmid_kernel *shm_rmid(int id)
return (struct shmid_kernel *)ipc_rmid(&shm_ids,id);
}
-static __inline__ int shm_addid(struct shmid_kernel *shp)
+static inline int shm_addid(struct shmid_kernel *shp)
{
return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni+1);
}
@@ -373,7 +371,7 @@ static int shm_statfs(struct super_block *sb, struct statfs *buf)
return 0;
}
-static void shm_write_inode(struct inode * inode)
+static void shm_write_inode(struct inode * inode, int unused)
{
}
@@ -544,13 +542,37 @@ static int shm_unlink (struct inode *dir, struct dentry *dent)
return 0;
}
-#define SHM_ENTRY(shp, index) (shp)->shm_dir[(index)/PTRS_PER_PTE][(index)%PTRS_PER_PTE]
+/*
+ * We cannot use kmalloc for shm_alloc since this restricts the
+ * maximum size of the segments.
+ *
+ * We also cannot use vmalloc, since this uses too much of the vmalloc
+ * space and we run out of this on highend machines.
+ *
+ * So we have to use this complicated indirect scheme to alloc the shm
+ * page tables.
+ *
+ */
+
+#ifdef PTE_INIT
+static inline void init_ptes (pte_t *pte, int number) {
+ while (number--)
+ PTE_INIT (pte++);
+}
+#else
+static inline void init_ptes (pte_t *pte, int number) {
+ memset (pte, 0, number*sizeof(*pte));
+}
+#endif
+
+#define PTES_PER_PAGE (PAGE_SIZE/sizeof(pte_t))
+#define SHM_ENTRY(shp, index) (shp)->shm_dir[(index)/PTES_PER_PAGE][(index)%PTES_PER_PAGE]
static pte_t **shm_alloc(unsigned long pages, int doacc)
{
- unsigned short dir = pages / PTRS_PER_PTE;
- unsigned short last = pages % PTRS_PER_PTE;
- pte_t **ret, **ptr, *pte;
+ unsigned short dir = pages / PTES_PER_PAGE;
+ unsigned short last = pages % PTES_PER_PAGE;
+ pte_t **ret, **ptr;
if (pages == 0)
return NULL;
@@ -564,8 +586,7 @@ static pte_t **shm_alloc(unsigned long pages, int doacc)
*ptr = (pte_t *)__get_free_page (GFP_KERNEL);
if (!*ptr)
goto free;
- for (pte = *ptr; pte < *ptr + PTRS_PER_PTE; pte++)
- pte_clear (pte);
+ init_ptes (*ptr, PTES_PER_PAGE);
}
/* The last one is probably not of PAGE_SIZE: we use kmalloc */
@@ -573,8 +594,7 @@ static pte_t **shm_alloc(unsigned long pages, int doacc)
*ptr = kmalloc (last*sizeof(pte_t), GFP_KERNEL);
if (!*ptr)
goto free;
- for (pte = *ptr; pte < *ptr + last; pte++)
- pte_clear (pte);
+ init_ptes (*ptr, last);
}
if (doacc) {
shm_lockall();
@@ -597,14 +617,14 @@ nomem:
static void shm_free(pte_t** dir, unsigned long pages, int doacc)
{
int i, rss, swp;
- pte_t **ptr = dir+pages/PTRS_PER_PTE;
+ pte_t **ptr = dir+pages/PTES_PER_PAGE;
if (!dir)
return;
for (i = 0, rss = 0, swp = 0; i < pages ; i++) {
pte_t pte;
- pte = dir[i/PTRS_PER_PTE][i%PTRS_PER_PTE];
+ pte = dir[i/PTES_PER_PAGE][i%PTES_PER_PAGE];
if (pte_none(pte))
continue;
if (pte_present(pte)) {
@@ -617,7 +637,7 @@ static void shm_free(pte_t** dir, unsigned long pages, int doacc)
}
/* first the last page */
- if (pages%PTRS_PER_PTE)
+ if (pages%PTES_PER_PAGE)
kfree (*ptr);
/* now the whole pages */
while (--ptr >= dir)
@@ -663,10 +683,10 @@ static int shm_setattr (struct dentry *dentry, struct iattr *attr)
BUG();
error = -ENOSPC;
if (shm_tot - shp->shm_npages >= shm_ctlall)
- goto out;
+ goto size_out;
error = 0;
if (shp->shm_segsz == attr->ia_size)
- goto out;
+ goto size_out;
/* Now we set them to the real values */
old_dir = shp->shm_dir;
old_pages = shp->shm_npages;
@@ -674,8 +694,8 @@ static int shm_setattr (struct dentry *dentry, struct iattr *attr)
pte_t *swap;
int i,j;
i = old_pages < new_pages ? old_pages : new_pages;
- j = i % PTRS_PER_PTE;
- i /= PTRS_PER_PTE;
+ j = i % PTES_PER_PAGE;
+ i /= PTES_PER_PAGE;
if (j)
memcpy (new_dir[i], old_dir[i], j * sizeof (pte_t));
while (i--) {
@@ -687,10 +707,21 @@ static int shm_setattr (struct dentry *dentry, struct iattr *attr)
shp->shm_dir = new_dir;
shp->shm_npages = new_pages;
shp->shm_segsz = attr->ia_size;
-out:
+size_out:
shm_unlock(inode->i_ino);
shm_free (old_dir, old_pages, 1);
+
set_attr:
+ if (!(shp = shm_lock(inode->i_ino)))
+ BUG();
+ if (attr->ia_valid & ATTR_MODE)
+ shp->shm_perm.mode = attr->ia_mode;
+ if (attr->ia_valid & ATTR_UID)
+ shp->shm_perm.uid = attr->ia_uid;
+ if (attr->ia_valid & ATTR_GID)
+ shp->shm_perm.gid = attr->ia_gid;
+ shm_unlock (inode->i_ino);
+
inode_setattr(inode, attr);
return error;
}
@@ -1073,6 +1104,9 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
case IPC_SET:
{
+ struct dentry * dentry;
+ char name[SHM_FMT_LEN+1];
+
if ((shmid % SEQ_MULTIPLIER)== zero_id)
return -EINVAL;
@@ -1098,7 +1132,29 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
shp->shm_flags = (shp->shm_flags & ~S_IRWXUGO)
| (setbuf.mode & S_IRWXUGO);
shp->shm_ctim = CURRENT_TIME;
- break;
+ shm_unlock(shmid);
+ up(&shm_ids.sem);
+
+ sprintf (name, SHM_FMT, shmid);
+ lock_kernel();
+ dentry = lookup_one(name, lock_parent(shm_sb->s_root));
+ unlock_dir(shm_sb->s_root);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto bad_dentry;
+ err = -ENOENT;
+ if (dentry->d_inode) {
+ struct inode *ino = dentry->d_inode;
+ ino->i_uid = setbuf.uid;
+ ino->i_gid = setbuf.gid;
+ ino->i_mode = (setbuf.mode & S_IRWXUGO) | (ino->i_mode & ~S_IALLUGO);;
+ ino->i_atime = ino->i_mtime = ino->i_ctime = CURRENT_TIME;
+ err = 0;
+ }
+ dput (dentry);
+ bad_dentry:
+ unlock_kernel();
+ return err;
}
default:
@@ -1142,6 +1198,7 @@ static int shm_mmap(struct file * file, struct vm_area_struct * vma)
*/
asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
{
+ struct shmid_kernel *shp;
unsigned long addr;
struct file * file;
int err;
@@ -1169,13 +1226,25 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
if (shmflg & SHM_RDONLY) {
prot = PROT_READ;
o_flags = O_RDONLY;
- acc_mode = MAY_READ;
+ acc_mode = S_IRUGO;
} else {
prot = PROT_READ | PROT_WRITE;
o_flags = O_RDWR;
- acc_mode = MAY_READ | MAY_WRITE;
+ acc_mode = S_IRUGO | S_IWUGO;
}
+ /*
+ * We cannot rely on the fs check since SYSV IPC does have an
+ * aditional creator id...
+ */
+ shp = shm_lock(shmid);
+ if(shp==NULL)
+ return -EINVAL;
+ err = ipcperms(&shp->shm_perm, acc_mode);
+ shm_unlock(shmid);
+ if (err)
+ return -EACCES;
+
sprintf (name, SHM_FMT, shmid);
lock_kernel();
@@ -1188,9 +1257,6 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
err = -ENOENT;
if (!dentry->d_inode)
goto bad_file;
- err = permission(dentry->d_inode, acc_mode);
- if (err)
- goto bad_file1;
file = dentry_open(dentry, shm_fs_type.kern_mnt, o_flags);
err = PTR_ERR(file);
if (IS_ERR (file))
@@ -1492,7 +1558,7 @@ int shm_swap (int prio, int gfp_mask)
shm_lockall();
check_id:
shp = shm_get(swap_id);
- if(shp==NULL || shp->shm_flags & SHM_LOCKED) {
+ if(shp==NULL || shp->shm_flags & PRV_LOCKED) {
next_id:
swap_idx = 0;
if (++swap_id > shm_ids.max_id) {
diff --git a/ipc/util.c b/ipc/util.c
index 29819efbb..a72dc14e6 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -24,6 +24,13 @@
#include "util.h"
+/**
+ * ipc_init - initialise IPC subsystem
+ *
+ * The various system5 IPC resources (semaphores, messages and shared
+ * memory are initialised
+ */
+
void __init ipc_init (void)
{
sem_init();
@@ -32,6 +39,16 @@ void __init ipc_init (void)
return;
}
+/**
+ * ipc_init_ids - initialise IPC identifiers
+ * @ids: Identifier set
+ * @size: Number of identifiers
+ *
+ * Given a size for the ipc identifier range (limited below IPCMNI)
+ * set up the sequence range to use then allocate and initialise the
+ * array itself.
+ */
+
void __init ipc_init_ids(struct ipc_ids* ids, int size)
{
int i;
@@ -62,6 +79,14 @@ void __init ipc_init_ids(struct ipc_ids* ids, int size)
ids->entries[i].p = NULL;
}
+/**
+ * ipc_findkey - find a key in an ipc identifier set
+ * @ids: Identifier set
+ * @key: The key to find
+ *
+ * Returns the identifier if found or -1 if not.
+ */
+
int ipc_findkey(struct ipc_ids* ids, key_t key)
{
int id;
@@ -106,6 +131,18 @@ static int grow_ary(struct ipc_ids* ids, int newsize)
return ids->size;
}
+/**
+ * ipc_addid - add an IPC identifier
+ * @ids: IPC identifier set
+ * @new: new IPC permission set
+ * @size: new size limit for the id array
+ *
+ * Add an entry 'new' to the IPC arrays. The permissions object is
+ * initialised and the first free entry is set up and the id assigned
+ * is returned. The list is returned in a locked state on success.
+ * On failure the list is not locked and -1 is returned.
+ */
+
int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
{
int id;
@@ -133,6 +170,17 @@ found:
return id;
}
+/**
+ * ipc_rmid - remove an IPC identifier
+ * @ids: identifier set
+ * @id: Identifier to remove
+ *
+ * The identifier must be valid, and in use. The kernel will panic if
+ * fed an invalid identifier. The entry is removed and internal
+ * variables recomputed. The object associated with the identifier
+ * is returned.
+ */
+
struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
{
struct kern_ipc_perm* p;
@@ -156,6 +204,14 @@ struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
return p;
}
+/**
+ * ipc_alloc - allocate ipc space
+ * @size: size desired
+ *
+ * Allocate memory from the appropriate pools and return a pointer to it.
+ * NULL is returned if the allocation fails
+ */
+
void* ipc_alloc(int size)
{
void* out;
@@ -166,6 +222,15 @@ void* ipc_alloc(int size)
return out;
}
+/**
+ * ipc_free - free ipc space
+ * @ptr: pointer returned by ipc_alloc
+ * @size: size of block
+ *
+ * Free a block created with ipc_alloc. The caller must know the size
+ * used in the allocation call.
+ */
+
void ipc_free(void* ptr, int size)
{
if(size > PAGE_SIZE)
@@ -174,10 +239,15 @@ void ipc_free(void* ptr, int size)
kfree(ptr);
}
-/*
- * Check user, group, other permissions for access
- * to ipc resources. return 0 if allowed
+/**
+ * ipcperms - check IPC permissions
+ * @ipcp: IPC permission set
+ * @flag: desired permission set.
+ *
+ * Check user, group, other permissions for access
+ * to ipc resources. return 0 if allowed
*/
+
int ipcperms (struct kern_ipc_perm *ipcp, short flag)
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
int requested_mode, granted_mode;
@@ -201,6 +271,16 @@ int ipcperms (struct kern_ipc_perm *ipcp, short flag)
* old/new ipc_perm structures
*/
+/**
+ * kernel_to_ipc64_perm - convert kernel ipc permissions to user
+ * @in: kernel permissions
+ * @out: new style IPC permissions
+ *
+ * Turn the kernel object 'in' into a set of permissions descriptions
+ * for returning to userspace (out).
+ */
+
+
void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
{
out->key = in->key;
@@ -212,6 +292,15 @@ void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
out->seq = in->seq;
}
+/**
+ * ipc64_perm_to_ipc_perm - convert old ipc permissions to new
+ * @in: new style IPC permissions
+ * @out: old style IPC permissions
+ *
+ * Turn the new style permissions object in into a compatibility
+ * object and store it into the 'out' pointer.
+ */
+
void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
{
out->key = in->key;
@@ -223,6 +312,15 @@ void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
out->seq = in->seq;
}
+/**
+ * ipc_parse_version - IPC call version
+ * @cmd: pointer to command
+ *
+ * Return IPC_64 for new style IPC and IPC_OLD for old style IPC.
+ * The cmd value is turned from an encoding command and version into
+ * just the command code.
+ */
+
int ipc_parse_version (int *cmd)
{
if (*cmd & IPC_64) {
@@ -310,7 +408,7 @@ void shm_unuse(swp_entry_t entry, struct page *page)
int map_zero_setup(struct vm_area_struct *vma)
{
- return -EINVAL;
+ return -ENOSYS;
}
#endif /* CONFIG_SYSVIPC */