linux-handle-short-reads-gracefully-20040504
[openafs.git] / src / afs / LINUX / osi_misc.c
index 1f22161..8a62d08 100644 (file)
  * Linux support routines.
  *
  */
-#include "../afs/param.h"
-#include "../afs/sysincludes.h"
-#include "../afs/afsincludes.h"
-#include "../afs/afs_stats.h"
+#include <afsconfig.h>
+#include "afs/param.h"
+
+RCSID
+    ("$Header$");
+
+#include "afs/sysincludes.h"
+#include "afsincludes.h"
+#include "afs/afs_stats.h"
 #if defined(AFS_LINUX24_ENV)
-#include "../h/smp_lock.h"
+#include "h/smp_lock.h"
+#endif
+#if defined(AFS_LINUX26_ENV)
+#include "h/namei.h"
 #endif
 
-char *crash_addr = 0; /* Induce an oops by writing here. */
-
+#if defined(AFS_LINUX24_ENV)
 /* Lookup name and return vnode for same. */
-int osi_lookupname(char *aname, uio_seg_t seg, int followlink,
-              vnode_t **dirvpp, struct dentry **dpp)
+int
+osi_lookupname(char *aname, uio_seg_t seg, int followlink,
+                       vnode_t ** dirvpp, struct dentry **dpp)
 {
-#if defined(AFS_LINUX24_ENV)
-    struct nameidata nd;
-#else
-    struct dentry *dp = NULL;
-#endif
     int code;
+    extern struct nameidata afs_cacheNd;
+    struct nameidata *nd = &afs_cacheNd;
 
     code = ENOENT;
-#if defined(AFS_LINUX24_ENV)
     if (seg == AFS_UIOUSER) {
-        code = followlink ?
-           user_path_walk(aname, &nd) : user_path_walk_link(aname, &nd);
-    }
-    else {
-        if (path_init(aname, followlink ? LOOKUP_FOLLOW : 0, &nd))
-           code = path_walk(aname, &nd);
+       code =
+           followlink ? user_path_walk(aname,
+                                       nd) : user_path_walk_link(aname, nd);
+    } else {
+#if defined(AFS_LINUX26_ENV)
+       code = path_lookup(aname, followlink ? LOOKUP_FOLLOW : 0, nd);
+#else
+       if (path_init(aname, followlink ? LOOKUP_FOLLOW : 0, nd))
+           code = path_walk(aname, nd);
+#endif
     }
 
     if (!code) {
-       if (nd.dentry->d_inode) {
-           *dpp = nd.dentry;
+       if (nd->dentry->d_inode) {
+           *dpp = dget(nd->dentry);
            code = 0;
+       } else {
+           code = ENOENT;
+           path_release(nd);
        }
-       else
-           path_release(&nd);
     }
+    return code;
+}
 #else
+int
+osi_lookupname(char *aname, uio_seg_t seg, int followlink, vnode_t ** dirvpp,
+              struct dentry **dpp)
+{
+    struct dentry *dp = NULL;
+    int code;
+
+    code = ENOENT;
     if (seg == AFS_UIOUSER) {
        dp = followlink ? namei(aname) : lnamei(aname);
-    }
-    else {
+    } else {
        dp = lookup_dentry(aname, NULL, followlink ? 1 : 0);
     }
 
@@ -63,17 +81,17 @@ int osi_lookupname(char *aname, uio_seg_t seg, int followlink,
        if (dp->d_inode) {
            *dpp = dp;
            code = 0;
-       }
-       else
+       } else
            dput(dp);
     }
-#endif
-           
+
     return code;
 }
+#endif
 
 /* Intialize cache device info and fragment size for disk cache partition. */
-int osi_InitCacheInfo(char *aname)
+int
+osi_InitCacheInfo(char *aname)
 {
     int code;
     struct dentry *dp;
@@ -81,13 +99,13 @@ int osi_InitCacheInfo(char *aname)
     extern struct osi_dev cacheDev;
     extern afs_int32 afs_fsfragsize;
     extern struct super_block *afs_cacheSBp;
-
     code = osi_lookupname(aname, AFS_UIOSYS, 1, NULL, &dp);
-    if (code) return ENOENT;
+    if (code)
+       return ENOENT;
 
     cacheInode = dp->d_inode->i_ino;
-    cacheDev.dev = dp->d_inode->i_dev;
-    afs_fsfragsize = dp->d_inode->i_sb->s_blocksize;
+    cacheDev.dev = dp->d_inode->i_sb->s_dev;
+    afs_fsfragsize = dp->d_inode->i_sb->s_blocksize - 1;
     afs_cacheSBp = dp->d_inode->i_sb;
 
     dput(dp);
@@ -103,44 +121,50 @@ int osi_InitCacheInfo(char *aname)
  * Seek, then read or write to an open inode. addrp points to data in
  * kernel space.
  */
-int osi_rdwr(int rw, struct osi_file *file, caddr_t addrp, size_t asize,
-            size_t *resid)
+int
+osi_rdwr(int rw, struct osi_file *file, caddr_t addrp, size_t asize,
+        size_t * resid)
 {
     int code = 0;
     KERNEL_SPACE_DECL;
     struct file *filp = &file->file;
     off_t offset = file->offset;
+    unsigned long savelim;
 
     /* Seek to the desired position. Return -1 on error. */
     if (filp->f_op->llseek) {
-       if (filp->f_op->llseek(filp, (loff_t)offset, 0) != offset)
+       if (filp->f_op->llseek(filp, (loff_t) offset, 0) != offset)
            return -1;
-    }
-    else
+    } else
        filp->f_pos = offset;
 
+    savelim = current->rlim[RLIMIT_FSIZE].rlim_cur;
+    current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
+
     /* Read/Write the data. */
     TO_USER_SPACE();
     if (rw == UIO_READ)
        code = FOP_READ(filp, addrp, asize);
     else if (rw == UIO_WRITE)
        code = FOP_WRITE(filp, addrp, asize);
-    else /* all is well? */
+    else                       /* all is well? */
        code = asize;
     TO_KERNEL_SPACE();
 
-    if (code >=0) {
+    current->rlim[RLIMIT_FSIZE].rlim_cur = savelim;
+
+    if (code >= 0) {
        *resid = asize - code;
        return 0;
-    }
-    else
+    } else
        return -1;
 }
 
 /* This variant is called from AFS read/write routines and takes a uio
  * struct and, if successful, returns 0.
  */
-int osi_file_uio_rdwr(struct osi_file *osifile, uio_t *uiop, int rw)
+int
+osi_file_uio_rdwr(struct osi_file *osifile, uio_t * uiop, int rw)
 {
     struct file *filp = &osifile->file;
     struct inode *ip = FILE_INODE(&osifile->file);
@@ -148,6 +172,10 @@ int osi_file_uio_rdwr(struct osi_file *osifile, uio_t *uiop, int rw)
     int code = 0;
     struct iovec *iov;
     int count;
+    unsigned long savelim;
+
+    savelim = current->rlim[RLIMIT_FSIZE].rlim_cur;
+    current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
 
     if (uiop->uio_seg == AFS_UIOSYS)
        TO_USER_SPACE();
@@ -161,7 +189,7 @@ int osi_file_uio_rdwr(struct osi_file *osifile, uio_t *uiop, int rw)
            uiop->uio_iovcnt--;
            continue;
        }
-       
+
        if (rw == UIO_READ)
            code = FOP_READ(filp, iov->iov_base, count);
        else
@@ -170,8 +198,15 @@ int osi_file_uio_rdwr(struct osi_file *osifile, uio_t *uiop, int rw)
        if (code < 0) {
            code = -code;
            break;
+       } else if (code == 0) {
+           /*
+            * This is bad -- we can't read any more data from the
+            * file, but we have no good way of signaling a partial
+            * read either.
+           code = EIO;
+           break;
        }
-       
+
        iov->iov_base += code;
        iov->iov_len -= code;
        uiop->uio_resid -= code;
@@ -182,15 +217,17 @@ int osi_file_uio_rdwr(struct osi_file *osifile, uio_t *uiop, int rw)
     if (uiop->uio_seg == AFS_UIOSYS)
        TO_KERNEL_SPACE();
 
+    current->rlim[RLIMIT_FSIZE].rlim_cur = savelim;
+
     return code;
 }
 
 /* setup_uio 
  * Setup a uio struct.
  */
-void setup_uio(uio_t *uiop, struct iovec *iovecp, char *buf,
-                            int pos, int count, uio_flag_t flag,
-                            uio_seg_t seg)
+void
+setup_uio(uio_t * uiop, struct iovec *iovecp, char *buf, afs_offs_t pos,
+         int count, uio_flag_t flag, uio_seg_t seg)
 {
     iovecp->iov_base = buf;
     iovecp->iov_len = count;
@@ -207,7 +244,8 @@ void setup_uio(uio_t *uiop, struct iovec *iovecp, char *buf,
  * UIO_READ : dp -> uio
  * UIO_WRITE : uio -> dp
  */
-int uiomove(char *dp, int length, uio_flag_t rw, uio_t *uiop)
+int
+uiomove(char *dp, int length, uio_flag_t rw, uio_t * uiop)
 {
     int count, n;
     struct iovec *iov;
@@ -225,25 +263,29 @@ int uiomove(char *dp, int length, uio_flag_t rw, uio_t *uiop)
 
        if (count > length)
            count = length;
-       
-       switch(uiop->uio_seg) {
+
+       switch (uiop->uio_seg) {
        case AFS_UIOSYS:
-           switch(rw) {
+           switch (rw) {
            case UIO_READ:
-               memcpy(iov->iov_base, dp, count); break;
+               memcpy(iov->iov_base, dp, count);
+               break;
            case UIO_WRITE:
-               memcpy(dp, iov->iov_base, count); break;
+               memcpy(dp, iov->iov_base, count);
+               break;
            default:
                printf("uiomove: Bad rw = %d\n", rw);
                return -EINVAL;
            }
            break;
        case AFS_UIOUSER:
-           switch(rw) {
+           switch (rw) {
            case UIO_READ:
-               AFS_COPYOUT(dp, iov->iov_base, count, code); break;
+               AFS_COPYOUT(dp, iov->iov_base, count, code);
+               break;
            case UIO_WRITE:
-               AFS_COPYIN(iov->iov_base, dp, count, code); break;
+               AFS_COPYIN(iov->iov_base, dp, count, code);
+               break;
            default:
                printf("uiomove: Bad rw = %d\n", rw);
                return -EINVAL;
@@ -264,22 +306,24 @@ int uiomove(char *dp, int length, uio_flag_t rw, uio_t *uiop)
     return 0;
 }
 
-void afs_osi_SetTime(osi_timeval_t *tvp)
+void
+afs_osi_SetTime(osi_timeval_t * tvp)
 {
-    extern int (*sys_settimeofdayp)(struct timeval *tv, struct timezone *tz);
+    extern int (*sys_settimeofdayp) (struct timeval * tv,
+                                    struct timezone * tz);
 #ifdef AFS_LINUX_64BIT_KERNEL
     struct timeval tv;
     AFS_STATCNT(osi_SetTime);
     tv.tv_sec = tvp->tv_sec;
     tv.tv_usec = tvp->tv_usec;
-    (void) (*sys_settimeofdayp)(&tv, NULL);
+    (void)(*sys_settimeofdayp) (&tv, NULL);
 #else
     KERNEL_SPACE_DECL;
 
     AFS_STATCNT(osi_SetTime);
 
     TO_USER_SPACE();
-    (void) (*sys_settimeofdayp)(tvp, NULL);
+    (void)(*sys_settimeofdayp) (tvp, NULL);
     TO_KERNEL_SPACE();
 #endif
 }
@@ -287,23 +331,26 @@ void afs_osi_SetTime(osi_timeval_t *tvp)
 /* Free all the pages on any of the vnodes in the vlru. Must be done before
  * freeing all memory.
  */
-void osi_linux_free_inode_pages(void)
+void
+osi_linux_free_inode_pages(void)
 {
     int i;
     struct vcache *tvc;
     struct inode *ip;
     extern struct vcache *afs_vhashT[VCSIZE];
 
-    for (i=0; i<VCSIZE; i++) {
-       for(tvc = afs_vhashT[i]; tvc; tvc=tvc->hnext) {
-           ip = (struct inode*)tvc;
+    for (i = 0; i < VCSIZE; i++) {
+       for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
+           ip = AFSTOI(tvc);
 #if defined(AFS_LINUX24_ENV)
            if (ip->i_data.nrpages) {
 #else
            if (ip->i_nrpages) {
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,15)
-               truncate_inode_pages(&ip->i_data, 0);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               truncate_inode_pages(&ip->i_data, 0);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,15)
+               truncate_inode_pages(ip, 0);
 #else
                invalidate_inode_pages(ip);
 #endif
@@ -320,65 +367,68 @@ void osi_linux_free_inode_pages(void)
     }
 }
 
-void osi_clear_inode(struct inode *ip)
+void
+osi_clear_inode(struct inode *ip)
 {
     cred_t *credp = crref();
-    struct vcache *vc = (struct vcache*)ip;
+    struct vcache *vcp = ITOAFS(ip);
 
 #if defined(AFS_LINUX24_ENV)
     if (atomic_read(&ip->i_count) > 1)
 #else
     if (ip->i_count > 1)
 #endif
-        printf("afs_put_inode: ino %d (0x%x) has count %d\n", ip->i_ino, ip);
-
-    afs_InactiveVCache(vc, credp);
-#if defined(AFS_LINUX24_ENV)
-    atomic_set(&ip->i_count, 0);
-#else
-    ip->i_count = 0;
-#endif
-    ip->i_nlink = 0; /* iput checks this after calling this routine. */
+       printf("afs_put_inode: ino %d (0x%x) has count %d\n", ip->i_ino, ip,
+              ip->i_count);
+
+    afs_InactiveVCache(vcp, credp);
+    ObtainWriteLock(&vcp->lock, 504);
+    ip->i_nlink = 0;           /* iput checks this after calling this routine. */
+    ip->i_state = I_CLEAR;
+    ReleaseWriteLock(&vcp->lock);
     crfree(credp);
 }
 
+#if !defined(AFS_LINUX26_ENV)
 /* iput an inode. Since we still have a separate inode pool, we don't want
  * to call iput on AFS inodes, since they would then end up on Linux's
  * inode_unsed list.
  */
-void osi_iput(struct inode *ip)
+void
+osi_iput(struct inode *ip)
 {
     extern struct vfs *afs_globalVFS;
 
     AFS_GLOCK();
+
+    if (afs_globalVFS && ip->i_sb != afs_globalVFS)
+       osi_Panic("IPUT Not an afs inode\n");
+
 #if defined(AFS_LINUX24_ENV)
-    if (atomic_read(&ip->i_count) == 0 || atomic_read(&ip->i_count) & 0xffff0000) {
+    if (atomic_read(&ip->i_count) == 0)
 #else
-    if (ip->i_count == 0 || ip->i_count & 0xffff0000) {
+    if (ip->i_count == 0)
 #endif
        osi_Panic("IPUT Bad refCount %d on inode 0x%x\n",
 #if defined(AFS_LINUX24_ENV)
-                 atomic_read(&ip->i_count), ip);
+                 atomic_read(&ip->i_count),
 #else
-                 ip->i_count, ip);
+                 ip->i_count,
 #endif
-    }
-    if (afs_globalVFS && afs_globalVFS == ip->i_sb ) {
+                               ip);
+
 #if defined(AFS_LINUX24_ENV)
-       atomic_dec(&ip->i_count);
-       if (!atomic_read(&ip->i_count))
+    if (atomic_dec_and_test(&ip->i_count))
 #else
-       ip->i_count --;
-       if (!ip->i_count)
+    if (!--ip->i_count)
 #endif
-           osi_clear_inode(ip);
-        AFS_GUNLOCK();
-    }
-    else { 
-        AFS_GUNLOCK();
-       iput(ip);
+                                          {
+       osi_clear_inode(ip);
+       ip->i_state = 0;
     }
+    AFS_GUNLOCK();
 }
+#endif
 
 /* check_bad_parent() : Checks if this dentry's vcache is a root vcache
  * that has its mvid (parent dir's fid) pointer set to the wrong directory
@@ -396,29 +446,46 @@ void osi_iput(struct inode *ip)
  *    to the correct parent and mountpoint fids.
  */
 
-void check_bad_parent(struct dentry *dp)
+void
+check_bad_parent(struct dentry *dp)
 {
-  cred_t *credp;
-  struct vcache *vcp = (struct vcache*)dp->d_inode, *avc = NULL;
-  struct vcache *pvc = (struct vcache *)dp->d_parent->d_inode;
-
-  if (vcp->mvid->Fid.Volume != pvc->fid.Fid.Volume) { /* bad parent */
-    credp = crref();
-
-
-    /* force a lookup, so vcp->mvid is fixed up */
-    afs_lookup(pvc, dp->d_name.name, &avc, credp);
-    if (!avc || vcp != avc) {    /* bad, very bad.. */
-      afs_Trace4(afs_iclSetp, CM_TRACE_TMP_1S3L, ICL_TYPE_STRING,
-                "afs_linux_revalidate : bad pointer returned from afs_lookup origvc newvc dentry",
-                ICL_TYPE_POINTER, vcp,
-                ICL_TYPE_POINTER, avc,
-                ICL_TYPE_POINTER, dp);
+    cred_t *credp;
+    struct vcache *vcp = ITOAFS(dp->d_inode), *avc = NULL;
+    struct vcache *pvc = ITOAFS(dp->d_parent->d_inode);
+
+    if (vcp->mvid->Fid.Volume != pvc->fid.Fid.Volume) {        /* bad parent */
+       credp = crref();
+
+
+       /* force a lookup, so vcp->mvid is fixed up */
+       afs_lookup(pvc, dp->d_name.name, &avc, credp);
+       if (!avc || vcp != avc) {       /* bad, very bad.. */
+           afs_Trace4(afs_iclSetp, CM_TRACE_TMP_1S3L, ICL_TYPE_STRING,
+                      "check_bad_parent: bad pointer returned from afs_lookup origvc newvc dentry",
+                      ICL_TYPE_POINTER, vcp, ICL_TYPE_POINTER, avc,
+                      ICL_TYPE_POINTER, dp);
+       }
+       if (avc)
+           AFS_RELE(avc);
+       crfree(credp);
     }
-    if (avc)
-       AFS_RELE(avc);
-    crfree(credp);
-  } /* if bad parent */
-  return;
+    /* if bad parent */
+    return;
+}
+
+struct task_struct *rxk_ListenerTask;
+
+void
+osi_linux_mask(void)
+{
+    SIG_LOCK(current);
+    sigfillset(&current->blocked);
+    RECALC_SIGPENDING(current);
+    SIG_UNLOCK(current);
+}
+
+void
+osi_linux_rxkreg(void)
+{
+    rxk_ListenerTask = current;
 }