aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ocfs2/stackglue.c
diff options
context:
space:
mode:
authorJoel Becker <joel.becker@oracle.com>2008-02-01 17:45:08 -0500
committerMark Fasheh <mfasheh@suse.com>2008-04-18 11:56:04 -0400
commitde551246e7bc5558371c3427889a8db1b8cc60f4 (patch)
tree31ae24280b8c3517434a894c32e9aa5faae2a173 /fs/ocfs2/stackglue.c
parent0abd6d1803b01c741430af270026d1d95a103d9c (diff)
ocfs2: Remove CANCELGRANT from the view of dlmglue.
o2dlm has the non-standard behavior of providing a cancel callback (unlock_ast) even when the cancel has failed (the locking operation succeeded without canceling). This is called CANCELGRANT after the status code sent to the callback. fs/dlm does not provide this callback, so dlmglue must be changed to live without it. o2dlm_unlock_ast_wrapper() in stackglue now ignores CANCELGRANT calls. Because dlmglue no longer sees CANCELGRANT, ocfs2_unlock_ast() no longer needs to check for it. ocfs2_locking_ast() must catch that a cancel was tried and clear the cancel state. Making these changes opens up a locking race. dlmglue uses the the OCFS2_LOCK_BUSY flag to ensure only one thread is calling the dlm at any one time. But dlmglue must unlock the lockres before calling into the dlm. In the small window of time between unlocking the lockres and calling the dlm, the downconvert thread can try to cancel the lock. The downconvert thread is checking the OCFS2_LOCK_BUSY flag - it doesn't know that ocfs2_dlm_lock() has not yet been called. Because ocfs2_dlm_lock() has not yet been called, the cancel operation will just be a no-op. There's nothing to cancel. With CANCELGRANT, dlmglue uses the CANCELGRANT callback to clear up the cancel state. When it comes around again, it will retry the cancel. Eventually, the first thread will have called into ocfs2_dlm_lock(), and either the lock or the cancel will succeed. The downconvert thread can then do its downconvert. Without CANCELGRANT, there is nothing to clean up the cancellation state. The downconvert thread does not know to retry its operations. More importantly, the original lock may be blocking on the other node that is trying to cancel us. With neither able to make progress, the ast is never called and the cancellation state is never cleaned up that way. dlmglue is deadlocked. The OCFS2_LOCK_PENDING flag is introduced to remedy this window. It is set at the same time OCFS2_LOCK_BUSY is. Thus, the downconvert thread can check whether the lock is cancelable. If not, it just loops around to try again. Once ocfs2_dlm_lock() is called, the thread then clears OCFS2_LOCK_PENDING and wakes the downconvert thread. Now, if the downconvert thread finds the lock BUSY, it can safely try to cancel it. Whether the cancel works or not, the state will be properly set and the lock processing can continue. Signed-off-by: Joel Becker <joel.becker@oracle.com> Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Diffstat (limited to 'fs/ocfs2/stackglue.c')
-rw-r--r--fs/ocfs2/stackglue.c40
1 files changed, 13 insertions, 27 deletions
diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c
index 670fa945c212..abdb9f6f4cc9 100644
--- a/fs/ocfs2/stackglue.c
+++ b/fs/ocfs2/stackglue.c
@@ -104,8 +104,8 @@ static int flags_to_o2dlm(u32 flags)
104 * 104 *
105 * DLM_NORMAL: 0 105 * DLM_NORMAL: 0
106 * DLM_NOTQUEUED: -EAGAIN 106 * DLM_NOTQUEUED: -EAGAIN
107 * DLM_CANCELGRANT: -DLM_ECANCEL 107 * DLM_CANCELGRANT: -EBUSY
108 * DLM_CANCEL: -DLM_EUNLOCK 108 * DLM_CANCEL: -DLM_ECANCEL
109 */ 109 */
110/* Keep in sync with dlmapi.h */ 110/* Keep in sync with dlmapi.h */
111static int status_map[] = { 111static int status_map[] = {
@@ -113,13 +113,13 @@ static int status_map[] = {
113 [DLM_GRANTED] = -EINVAL, 113 [DLM_GRANTED] = -EINVAL,
114 [DLM_DENIED] = -EACCES, 114 [DLM_DENIED] = -EACCES,
115 [DLM_DENIED_NOLOCKS] = -EACCES, 115 [DLM_DENIED_NOLOCKS] = -EACCES,
116 [DLM_WORKING] = -EBUSY, 116 [DLM_WORKING] = -EACCES,
117 [DLM_BLOCKED] = -EINVAL, 117 [DLM_BLOCKED] = -EINVAL,
118 [DLM_BLOCKED_ORPHAN] = -EINVAL, 118 [DLM_BLOCKED_ORPHAN] = -EINVAL,
119 [DLM_DENIED_GRACE_PERIOD] = -EACCES, 119 [DLM_DENIED_GRACE_PERIOD] = -EACCES,
120 [DLM_SYSERR] = -ENOMEM, /* It is what it is */ 120 [DLM_SYSERR] = -ENOMEM, /* It is what it is */
121 [DLM_NOSUPPORT] = -EPROTO, 121 [DLM_NOSUPPORT] = -EPROTO,
122 [DLM_CANCELGRANT] = -DLM_ECANCEL, /* Cancel after grant */ 122 [DLM_CANCELGRANT] = -EBUSY, /* Cancel after grant */
123 [DLM_IVLOCKID] = -EINVAL, 123 [DLM_IVLOCKID] = -EINVAL,
124 [DLM_SYNC] = -EINVAL, 124 [DLM_SYNC] = -EINVAL,
125 [DLM_BADTYPE] = -EINVAL, 125 [DLM_BADTYPE] = -EINVAL,
@@ -137,7 +137,7 @@ static int status_map[] = {
137 [DLM_VALNOTVALID] = -EINVAL, 137 [DLM_VALNOTVALID] = -EINVAL,
138 [DLM_REJECTED] = -EPERM, 138 [DLM_REJECTED] = -EPERM,
139 [DLM_ABORT] = -EINVAL, 139 [DLM_ABORT] = -EINVAL,
140 [DLM_CANCEL] = -DLM_EUNLOCK, /* Successful cancel */ 140 [DLM_CANCEL] = -DLM_ECANCEL, /* Successful cancel */
141 [DLM_IVRESHANDLE] = -EINVAL, 141 [DLM_IVRESHANDLE] = -EINVAL,
142 [DLM_DEADLOCK] = -EDEADLK, 142 [DLM_DEADLOCK] = -EDEADLK,
143 [DLM_DENIED_NOASTS] = -EINVAL, 143 [DLM_DENIED_NOASTS] = -EINVAL,
@@ -152,6 +152,7 @@ static int status_map[] = {
152 [DLM_MIGRATING] = -ERESTART, 152 [DLM_MIGRATING] = -ERESTART,
153 [DLM_MAXSTATS] = -EINVAL, 153 [DLM_MAXSTATS] = -EINVAL,
154}; 154};
155
155static int dlm_status_to_errno(enum dlm_status status) 156static int dlm_status_to_errno(enum dlm_status status)
156{ 157{
157 BUG_ON(status > (sizeof(status_map) / sizeof(status_map[0]))); 158 BUG_ON(status > (sizeof(status_map) / sizeof(status_map[0])));
@@ -175,38 +176,23 @@ static void o2dlm_blocking_ast_wrapper(void *astarg, int level)
175 176
176static void o2dlm_unlock_ast_wrapper(void *astarg, enum dlm_status status) 177static void o2dlm_unlock_ast_wrapper(void *astarg, enum dlm_status status)
177{ 178{
178 int error; 179 int error = dlm_status_to_errno(status);
179 180
180 BUG_ON(lproto == NULL); 181 BUG_ON(lproto == NULL);
181 182
182 /* 183 /*
183 * XXX: CANCEL values are sketchy.
184 *
185 * Currently we have preserved the o2dlm paradigm. You can get
186 * unlock_ast() whether the cancel succeded or not.
187 *
188 * First, we're going to pass DLM_EUNLOCK just like fs/dlm does for
189 * successful unlocks. That is a clean behavior.
190 *
191 * In o2dlm, you can get both the lock_ast() for the lock being 184 * In o2dlm, you can get both the lock_ast() for the lock being
192 * granted and the unlock_ast() for the CANCEL failing. A 185 * granted and the unlock_ast() for the CANCEL failing. A
193 * successful cancel sends DLM_NORMAL here. If the 186 * successful cancel sends DLM_NORMAL here. If the
194 * lock grant happened before the cancel arrived, you get 187 * lock grant happened before the cancel arrived, you get
195 * DLM_CANCELGRANT. For now, we'll use DLM_ECANCEL to signify 188 * DLM_CANCELGRANT.
196 * CANCELGRANT - the CANCEL was supposed to happen but didn't. We
197 * can then use DLM_EUNLOCK to signify a successful CANCEL -
198 * effectively, the CANCEL caused the lock to roll back.
199 * 189 *
200 * In the future, we will likely move the o2dlm to send only one 190 * There's no need for the double-ast. If we see DLM_CANCELGRANT,
201 * ast - either unlock_ast() for a successful CANCEL or lock_ast() 191 * we just ignore it. We expect the lock_ast() to handle the
202 * when the grant succeeds. At that point, we'll send DLM_ECANCEL 192 * granted lock.
203 * for all cancel results (CANCELGRANT will no longer exist).
204 */ 193 */
205 error = dlm_status_to_errno(status); 194 if (status == DLM_CANCELGRANT)
206 195 return;
207 /* Successful unlock is DLM_EUNLOCK */
208 if (!error)
209 error = -DLM_EUNLOCK;
210 196
211 lproto->lp_unlock_ast(astarg, error); 197 lproto->lp_unlock_ast(astarg, error);
212} 198}