osi_PrePopulateVCache(struct vcache *avc) {
memset(avc, 0, sizeof(struct vcache));
+ QInit(&avc->multiPage);
+
AFS_RWLOCK_INIT(&avc->vlock, "vcache vlock");
rw_init(&avc->rwlock, "vcache rwlock", RW_DEFAULT, NULL);
return code;
}
+/* Does this dcache conflict with a multiPage request for this vcache?
+ *
+ * This function only exists for Solaris. This is used by afs_GetDownD to
+ * calculate if trying to evict the given dcache may deadlock with an
+ * in-progress afs_getpage call that is trying to get more than one page at
+ * once. See afs_getpage for details. We return 0 if we do NOT conflict,
+ * nonzero otherwise. If we return nonzero, we should NOT try to evict the
+ * given dcache entry from the cache.
+ *
+ * Locking: tvc->vlock is write-locked on entry (and GLOCK is held)
+ */
+int
+osi_VM_MultiPageConflict(struct vcache *avc, struct dcache *adc)
+{
+ struct multiPage_range *range;
+ for (range = (struct multiPage_range *)avc->multiPage.next;
+ range != &avc->multiPage;
+ range = (struct multiPage_range *)QNext(&range->q)) {
+
+ if (adc->f.chunk >= AFS_CHUNK(range->off) &&
+ adc->f.chunk <= AFS_CHUNK(range->off + range->len - 1)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/* Try to discard pages, in order to recycle a vcache entry.
*
* We also make some sanity checks: ref count, open count, held locks.
code =
afs_GetOnePage(vp, off, len, protp, pl, plsz, seg, addr, rw, acred);
else {
+ struct multiPage_range range;
struct vcache *vcp = VTOAFS(vp);
+
+ /* We've been asked to get more than one page. We must return all
+ * requested pages at once, all of them locked, which means all of
+ * these dcache entries cannot be kicked out of the cache before we
+ * return (since their pages cannot be invalidated).
+ *
+ * afs_GetOnePage will be called multiple times by pvn_getpages in
+ * order to get all of the requested pages. One of the later
+ * afs_GetOnePage calls may need to evict some cache entries in order
+ * to perform its read. If we try to kick out one of the entries an
+ * earlier afs_GetOnePage call used, we will deadlock since we have
+ * the page locked. So, to tell afs_GetDownD that it should skip over
+ * any entries we've read in due to this afs_getpage call, record the
+ * offset and length in avc->multiPage.
+ *
+ * Ideally we would just set something in each dcache as we get it,
+ * but that is rather difficult, since pvn_getpages doesn't let us
+ * retain any information between calls to afs_GetOnePage. So instead
+ * just record the offset and length, and let afs_GetDownD calculate
+ * which dcache entries should be skipped. */
+
+ range.off = off;
+ range.len = len;
+
ObtainWriteLock(&vcp->vlock, 548);
- vcp->multiPage++;
+ QAdd(&vcp->multiPage, &range.q);
ReleaseWriteLock(&vcp->vlock);
code =
pvn_getpages(afs_GetOnePage, vp, off, len, protp, pl, plsz, seg, addr, rw, acred);
ObtainWriteLock(&vcp->vlock, 549);
- vcp->multiPage--;
+ QRemove(&range.q);
ReleaseWriteLock(&vcp->vlock);
}
AFS_GUNLOCK();
struct afs_vnuniq oldParent;
};
+#ifdef AFS_SUN5_ENV
+/*
+ * This is for the multiPage field in struct vcache. Each one of these
+ * represents an outstanding getpage request that is larger than a single page.
+ * Recording these is necessary to prevent afs_GetOnePage from trying to evict
+ * a dcache entry that an earlier afs_GetOnePage call got in the same getpage
+ * request. See osi_VM_MultiPageConflict and afs_getpage.
+ */
+struct multiPage_range {
+ struct afs_q q;
+ offset_t off; /**< offset of getpage request */
+ u_int len; /**< length of getpage request */
+};
+#endif
+
/* INVARIANTs: (vlruq.next != NULL) == (vlruq.prev != NULL)
* nextfree => !vlruq.next && ! vlruq.prev
* !(avc->nextfree) && !avc->vlruq.next => (FreeVCList == avc->nextfree)
afs_ucred_t *uncred;
int asynchrony; /* num kbytes to store behind */
#ifdef AFS_SUN5_ENV
- short multiPage; /* count of multi-page getpages in progress */
+ struct afs_q multiPage; /* list of multiPage_range structs */
#endif
int protocol; /* RX_FILESERVER, RX_OSD, ... defined in afsint.xg */
#if !defined(UKERNEL)
* we don't reclaim active entries, or other than target bucket.
* Set to 1, we reclaim even active ones in target bucket.
* Set to 2, we reclaim any inactive one.
- * Set to 3, we reclaim even active ones.
+ * Set to 3, we reclaim even active ones. On Solaris, we also reclaim
+ * entries whose corresponding vcache has a nonempty multiPage list, when
+ * possible.
*/
if (splitdcache) {
phase = 0;
ReleaseWriteLock(&afs_xdcache);
ObtainWriteLock(&tvc->vlock, 543);
- if (tvc->multiPage) {
- skip = 1;
- goto endmultipage;
+ if (!QEmpty(&tvc->multiPage)) {
+ if (phase < 3 || osi_VM_MultiPageConflict(tvc, tdc)) {
+ skip = 1;
+ goto endmultipage;
+ }
}
/* block locking pages */
tvc->vstates |= VPageCleaning;
#ifdef AFS_SUN5_ENV
extern int osi_VM_GetDownD(struct vcache *avc, struct dcache *adc);
+extern int osi_VM_MultiPageConflict(struct vcache *avc, struct dcache *adc);
extern void osi_VM_PreTruncate(struct vcache *avc, int alen,
afs_ucred_t *acred);
#endif