extern struct super_block *afs_cacheSBp;
#if defined(AFS_LINUX26_ENV)
-void *
-osi_UFSOpen(afs_dcache_id_t *ainode)
+struct file *
+afs_linux_raw_open(afs_dcache_id_t *ainode, ino_t *hint)
{
- struct osi_file *afile = NULL;
- extern int cacheDiskType;
struct inode *tip = NULL;
struct dentry *dp = NULL;
- struct file *filp = NULL;
+ struct file* filp;
- AFS_STATCNT(osi_UFSOpen);
- if (cacheDiskType != AFS_FCACHE_TYPE_UFS) {
- osi_Panic("UFSOpen called for non-UFS cache\n");
- }
- if (!afs_osicred_initialized) {
- /* valid for alpha_osf, SunOS, Ultrix */
- memset((char *)&afs_osi_cred, 0, sizeof(AFS_UCRED));
- crhold(&afs_osi_cred); /* don't let it evaporate, since it is static */
- afs_osicred_initialized = 1;
- }
- afile = (struct osi_file *)osi_AllocLargeSpace(sizeof(struct osi_file));
- AFS_GUNLOCK();
- if (!afile) {
- osi_Panic("osi_UFSOpen: Failed to allocate %d bytes for osi_file.\n",
- (int)sizeof(struct osi_file));
- }
- memset(afile, 0, sizeof(struct osi_file));
#if !defined(LINUX_USE_FH)
tip = iget(afs_cacheSBp, ainode->ufs);
if (!tip)
- osi_Panic("Can't get inode %d\n", ainode->ufs);
+ osi_Panic("Can't get inode %d\n", (int) ainode->ufs);
dp = d_alloc_anon(tip);
#else
- dp = afs_cacheSBp->s_export_op->fh_to_dentry(afs_cacheSBp, &ainode->ufs.fh, cache_fh_len, cache_fh_type);
- if (!dp)
- osi_Panic("Can't get dentry\n");
+ dp = afs_cacheSBp->s_export_op->fh_to_dentry(afs_cacheSBp, &ainode->ufs.fh,
+ cache_fh_len, cache_fh_type);
+ if (!dp)
+ osi_Panic("Can't get dentry\n");
tip = dp->d_inode;
#endif
tip->i_flags |= MS_NOATIME; /* Disable updating access times. */
#if defined(LINUX_USE_FH)
osi_Panic("Can't open file\n");
#else
- osi_Panic("Can't open inode %d\n", ainode->ufs);
+ osi_Panic("Can't open inode %d\n", (int) ainode->ufs);
#endif
- afile->filp = filp;
- afile->size = i_size_read(FILE_INODE(filp));
+ if (hint)
+ *hint = tip->i_ino;
+ return filp;
+}
+
+void *
+osi_UFSOpen(afs_dcache_id_t *ainode)
+{
+ struct osi_file *afile = NULL;
+ extern int cacheDiskType;
+ struct file *filp = NULL;
+ AFS_STATCNT(osi_UFSOpen);
+ if (cacheDiskType != AFS_FCACHE_TYPE_UFS) {
+ osi_Panic("UFSOpen called for non-UFS cache\n");
+ }
+ if (!afs_osicred_initialized) {
+ /* valid for alpha_osf, SunOS, Ultrix */
+ memset((char *)&afs_osi_cred, 0, sizeof(AFS_UCRED));
+ crhold(&afs_osi_cred); /* don't let it evaporate, since it is static */
+ afs_osicred_initialized = 1;
+ }
+ afile = (struct osi_file *)osi_AllocLargeSpace(sizeof(struct osi_file));
+ AFS_GUNLOCK();
+ if (!afile) {
+ osi_Panic("osi_UFSOpen: Failed to allocate %d bytes for osi_file.\n",
+ (int)sizeof(struct osi_file));
+ }
+ memset(afile, 0, sizeof(struct osi_file));
+
+ afile->filp = afs_linux_raw_open(ainode, &afile->inum);
+ afile->size = i_size_read(FILE_INODE(afile->filp));
AFS_GLOCK();
afile->offset = 0;
afile->proc = (int (*)())0;
- afile->inum = tip->i_ino; /* for hint validity checking */
return (void *)afile;
}
#else
return -EIO;
}
+/* Linux doesn't require a credp for many functions, and crref is an expensive
+ * operation. This helper function avoids obtaining it for VerifyVCache calls
+ */
+
+static inline int afs_linux_VerifyVCache(struct vcache *avc, cred_t **retcred) {
+ cred_t *credp = NULL;
+ struct vrequest treq;
+ int code;
+
+ if (avc->f.states & CStatd) {
+ if (retcred)
+ *retcred = NULL;
+ return 0;
+ }
+
+ credp = crref();
+
+ code = afs_InitReq(&treq, credp);
+ if (code == 0)
+ code = afs_VerifyVCache2(avc, &treq);
+
+ if (retcred != NULL)
+ *retcred = credp;
+ else
+ crfree(credp);
+
+ return afs_convert_code(code);
+}
+
static ssize_t
afs_linux_read(struct file *fp, char *buf, size_t count, loff_t * offp)
{
- ssize_t code;
+ ssize_t code = 0;
struct vcache *vcp = VTOAFS(fp->f_dentry->d_inode);
- cred_t *credp = crref();
- struct vrequest treq;
#if defined(AFS_CACHE_BYPASS) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
afs_size_t isize, offindex;
#endif
+
AFS_GLOCK();
afs_Trace4(afs_iclSetp, CM_TRACE_READOP, ICL_TYPE_POINTER, vcp,
ICL_TYPE_OFFSET, offp, ICL_TYPE_INT32, count, ICL_TYPE_INT32,
99999);
- /* get a validated vcache entry */
- code = afs_InitReq(&treq, credp);
- if (!code)
- code = afs_VerifyVCache(vcp, &treq);
+ code = afs_linux_VerifyVCache(vcp, NULL);
- if (code)
- code = afs_convert_code(code);
- else {
+ if (code == 0) {
#if defined(AFS_CACHE_BYPASS) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
isize = (i_size_read(fp->f_mapping->host) - 1) >> PAGE_CACHE_SHIFT;
offindex = *offp >> PAGE_CACHE_SHIFT;
goto done;
}
#endif
- osi_FlushPages(vcp, credp); /* ensure stale pages are gone */
+ /* Linux's FlushPages implementation doesn't ever use credp,
+ * so we optimise by not using it */
+ osi_FlushPages(vcp, NULL); /* ensure stale pages are gone */
AFS_GUNLOCK();
#ifdef DO_SYNC_READ
code = do_sync_read(fp, buf, count, offp);
done:
#endif
AFS_GUNLOCK();
- crfree(credp);
return code;
}
{
ssize_t code = 0;
struct vcache *vcp = VTOAFS(fp->f_dentry->d_inode);
- struct vrequest treq;
- cred_t *credp = crref();
+ cred_t *credp;
AFS_GLOCK();
ICL_TYPE_OFFSET, offp, ICL_TYPE_INT32, count, ICL_TYPE_INT32,
(fp->f_flags & O_APPEND) ? 99998 : 99999);
-
- /* get a validated vcache entry */
- code = (ssize_t) afs_InitReq(&treq, credp);
- if (!code)
- code = (ssize_t) afs_VerifyVCache(vcp, &treq);
+ code = afs_linux_VerifyVCache(vcp, &credp);
ObtainWriteLock(&vcp->lock, 529);
afs_FakeOpen(vcp);
ReleaseWriteLock(&vcp->lock);
- if (code)
- code = afs_convert_code(code);
- else {
+ if (code == 0) {
AFS_GUNLOCK();
#ifdef DO_SYNC_READ
code = do_sync_write(fp, buf, count, offp);
}
ObtainWriteLock(&vcp->lock, 530);
+
+ if (vcp->execsOrWriters == 1 && !credp)
+ credp = crref();
+
afs_FakeClose(vcp, credp);
ReleaseWriteLock(&vcp->lock);
ICL_TYPE_OFFSET, offp, ICL_TYPE_INT32, count, ICL_TYPE_INT32,
code);
+ if (credp)
+ crfree(credp);
AFS_GUNLOCK();
- crfree(credp);
return code;
}
AFS_GLOCK();
AFS_STATCNT(afs_readdir);
- code = afs_InitReq(&treq, credp);
+ code = afs_convert_code(afs_InitReq(&treq, credp));
crfree(credp);
- if (code) {
- code = afs_convert_code(code);
+ if (code)
goto out1;
- }
afs_InitFakeStat(&fakestat);
- code = afs_EvalFakeStat(&avc, &fakestat, &treq);
- if (code) {
- code = afs_convert_code(code);
+ code = afs_convert_code(afs_EvalFakeStat(&avc, &fakestat, &treq));
+ if (code)
goto out;
- }
/* update the cache entry */
tagain:
- code = afs_VerifyVCache(avc, &treq);
- if (code) {
- code = afs_convert_code(code);
+ code = afs_convert_code(afs_VerifyVCache2(avc, &treq));
+ if (code)
goto out;
- }
/* get a reference to the entire directory */
tdc = afs_GetDCache(avc, (afs_size_t) 0, &treq, &origOffset, &tlen, 1);
afs_linux_mmap(struct file *fp, struct vm_area_struct *vmap)
{
struct vcache *vcp = VTOAFS(FILE_INODE(fp));
- cred_t *credp = crref();
- struct vrequest treq;
int code;
AFS_GLOCK();
#endif
/* get a validated vcache entry */
- code = afs_InitReq(&treq, credp);
- if (code)
- goto out_err;
-
- code = afs_VerifyVCache(vcp, &treq);
- if (code)
- goto out_err;
-
- osi_FlushPages(vcp, credp); /* ensure stale pages are gone */
+ code = afs_linux_VerifyVCache(vcp, NULL);
+ /* Linux's Flushpage implementation doesn't use credp, so optimise
+ * our code to not need to crref() it */
+ osi_FlushPages(vcp, NULL); /* ensure stale pages are gone */
AFS_GUNLOCK();
code = generic_file_mmap(fp, vmap);
AFS_GLOCK();
if (!code)
vcp->f.states |= CMAPPED;
-out:
AFS_GUNLOCK();
- crfree(credp);
return code;
-
-out_err:
- code = afs_convert_code(code);
- goto out;
}
static int
cred_t *credp;
int code;
+ if (afs_shuttingdown)
+ return EIO;
+
#ifdef AFS_LINUX24_ENV
maybe_lock_kernel();
#endif
}
#endif
- credp = crref();
- code = afs_getattr(vcp, &vattr, credp);
+ /* This avoids the crref when we don't have to do it. Watch for
+ * changes in afs_getattr that don't get replicated here!
+ */
+ if (vcp->f.states & CStatd &&
+ (!afs_fakestat_enable || vcp->mvstat != 1) &&
+ !afs_nfsexporter) {
+ code = afs_CopyOutAttrs(vcp, &vattr);
+ } else {
+ credp = crref();
+ code = afs_getattr(vcp, &vattr, credp);
+ crfree(credp);
+ }
if (!code)
afs_fill_inode(AFSTOV(vcp), &vattr);
#ifdef AFS_LINUX24_ENV
maybe_unlock_kernel();
#endif
- crfree(credp);
return afs_convert_code(code);
}
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
#endif /* defined(AFS_CACHE_BYPASS */
+static int inline
+afs_linux_readpage_fastpath(struct file *fp, struct page *pp, int *codep)
+{
+ afs_offs_t offset = ((loff_t) pp->index) << PAGE_CACHE_SHIFT;
+ struct inode *ip = FILE_INODE(fp);
+ struct vcache *avc = VTOAFS(ip);
+ struct dcache *tdc;
+ struct file *cacheFp;
+ char *address;
+ int dcLocked;
+ ssize_t size;
+ mm_segment_t old_fs;
+
+ /* Not a UFS cache, don't do anything */
+ if (cacheDiskType != AFS_FCACHE_TYPE_UFS)
+ return 0;
+
+ /* Can't do anything if the vcache isn't statd , or if the read
+ * crosses a chunk boundary.
+ */
+ if (!(avc->f.states & CStatd) ||
+ AFS_CHUNK(offset) != AFS_CHUNK(offset + PAGE_SIZE)) {
+ return 0;
+ }
+
+ ObtainWriteLock(&avc->lock, 911);
+
+ /* XXX - See if hinting actually makes things faster !!! */
+
+ /* See if we have a suitable entry already cached */
+ tdc = avc->dchint;
+
+ if (tdc) {
+ /* We need to lock xdcache, then dcache, to handle situations where
+ * the hint is on the free list. However, we can't safely do this
+ * according to the locking hierarchy. So, use a non blocking lock.
+ */
+ ObtainReadLock(&afs_xdcache);
+ dcLocked = ( 0 == NBObtainReadLock(&tdc->lock));
+
+ if (dcLocked && (tdc->index != NULLIDX)
+ && !FidCmp(&tdc->f.fid, &avc->f.fid)
+ && tdc->f.chunk == AFS_CHUNK(offset)
+ && !(afs_indexFlags[tdc->index] & (IFFree | IFDiscarded))) {
+ /* Bonus - the hint was correct */
+ afs_RefDCache(tdc);
+ } else {
+ /* Only destroy the hint if its actually invalid, not if there's
+ * just been a locking failure */
+ if (dcLocked) {
+ ReleaseReadLock(&tdc->lock);
+ avc->dchint = NULL;
+ }
+
+ tdc = NULL;
+ dcLocked = 0;
+ }
+ ReleaseReadLock(&afs_xdcache);
+ }
+
+ /* No hint, or hint is no longer valid - see if we can get something
+ * directly from the dcache
+ */
+ if (!tdc)
+ tdc = afs_FindDCache(avc, offset);
+
+ if (!tdc) {
+ ReleaseWriteLock(&avc->lock);
+ return 0;
+ }
+
+ if (!dcLocked)
+ ObtainReadLock(&tdc->lock);
+
+ /* Is the dcache we've been given currently up to date */
+ if (!hsame(avc->f.m.DataVersion, tdc->f.versionNo) ||
+ (tdc->dflags & DFFetching)) {
+ ReleaseWriteLock(&avc->lock);
+ ReleaseReadLock(&tdc->lock);
+ afs_PutDCache(tdc);
+ return 0;
+ }
+
+ /* Update our hint for future abuse */
+ avc->dchint = tdc;
+
+ /* Okay, so we've now got a cache file that is up to date */
+
+ offset -= AFS_CHUNKTOBASE(tdc->f.chunk);
+
+ /* XXX - I suspect we should be locking the inodes before we use them! */
+ AFS_GUNLOCK();
+ cacheFp = afs_linux_raw_open(&tdc->f.inode, NULL);
+ if (cacheFp->f_op->llseek)
+ cacheFp->f_op->llseek(cacheFp, offset, 0);
+ else
+ cacheFp->f_pos = offset;
+
+ address = kmap(pp);
+ ClearPageError(pp);
+ old_fs = get_fs();
+ set_fs(get_ds());
+ size = cacheFp->f_op->read(cacheFp, address, PAGE_SIZE, &cacheFp->f_pos);
+ set_fs(old_fs);
+
+ if (size >= 0) {
+ if (size != PAGE_SIZE)
+ memset((void *)(address + size), 0, PAGE_SIZE - size);
+ size = 0;
+ flush_dcache_page(pp);
+ SetPageUptodate(pp);
+ }
+
+ kunmap(pp);
+ UnlockPage(pp);
+
+ filp_close(cacheFp, NULL);
+ AFS_GLOCK();
+
+ ReleaseReadLock(&tdc->lock);
+ ReleaseWriteLock(&avc->lock);
+ afs_PutDCache(tdc);
+
+ *codep = size;
+ return 1;
+}
/* afs_linux_readpage
* all reads come through here. A strategy-like read call.
afs_linux_readpage(struct file *fp, struct page *pp)
{
afs_int32 code;
- cred_t *credp = crref();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
char *address;
afs_offs_t offset = ((loff_t) pp->index) << PAGE_CACHE_SHIFT;
struct inode *ip = FILE_INODE(fp);
afs_int32 cnt = page_count(pp);
struct vcache *avc = VTOAFS(ip);
+ cred_t *credp;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ AFS_GLOCK();
+ if (afs_linux_readpage_fastpath(fp, pp, &code)) {
+ AFS_GUNLOCK();
+ return code;
+ }
+ AFS_GUNLOCK();
+#endif
+
+ credp = crref();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
address = kmap(pp);
ClearPageError(pp);