static struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres);
/*
- * Return value from ocfs2_convert_worker_t functions.
+ * Return value from ->downconvert_worker functions.
*
- * These control the precise actions of ocfs2_generic_unblock_lock()
+ * These control the precise actions of ocfs2_unblock_lock()
* and ocfs2_process_blocked_lock()
*
*/
enum ocfs2_unblock_action unblock_action;
};
-static int ocfs2_unblock_meta(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl);
-static int ocfs2_unblock_data(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl);
-static int ocfs2_unblock_inode_lock(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl);
-static int ocfs2_unblock_dentry_lock(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl);
-static int ocfs2_unblock_osb_lock(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl);
+static int ocfs2_check_meta_downconvert(struct ocfs2_lock_res *lockres,
+ int new_level);
+static void ocfs2_set_meta_lvb(struct ocfs2_lock_res *lockres);
+
+static int ocfs2_data_convert_worker(struct ocfs2_lock_res *lockres,
+ int blocking);
+
+static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,
+ int blocking);
static void ocfs2_dentry_post_unlock(struct ocfs2_super *osb,
struct ocfs2_lock_res *lockres);
* OCFS2 Lock Resource Operations
*
* These fine tune the behavior of the generic dlmglue locking infrastructure.
+ *
+ * The most basic of lock types can point ->l_priv to their respective
+ * struct ocfs2_super and allow the default actions to manage things.
+ *
+ * Right now, each lock type also needs to implement an init function,
+ * and trivial lock/unlock wrappers. ocfs2_simple_drop_lockres()
+ * should be called when the lock is no longer needed (i.e., object
+ * destruction time).
*/
struct ocfs2_lock_res_ops {
/*
* this callback if ->l_priv is not an ocfs2_super pointer
*/
struct ocfs2_super * (*get_osb)(struct ocfs2_lock_res *);
- int (*unblock)(struct ocfs2_lock_res *, struct ocfs2_unblock_ctl *);
+
+ /*
+ * Optionally called in the downconvert (or "vote") thread
+ * after a successful downconvert. The lockres will not be
+ * referenced after this callback is called, so it is safe to
+ * free memory, etc.
+ *
+ * The exact semantics of when this is called are controlled
+ * by ->downconvert_worker()
+ */
void (*post_unlock)(struct ocfs2_super *, struct ocfs2_lock_res *);
/*
*/
void (*set_lvb)(struct ocfs2_lock_res *);
+ /*
+ * Called from the downconvert thread when it is determined
+ * that a lock will be downconverted. This is called without
+ * any locks held so the function can do work that might
+ * schedule (syncing out data, etc).
+ *
+ * This should return any one of the ocfs2_unblock_action
+ * values, depending on what it wants the thread to do.
+ */
+ int (*downconvert_worker)(struct ocfs2_lock_res *, int);
+
/*
* LOCK_TYPE_* flags which describe the specific requirements
* of a lock type. Descriptions of each individual flag follow.
*/
#define LOCK_TYPE_USES_LVB 0x2
-typedef int (ocfs2_convert_worker_t)(struct ocfs2_lock_res *, int);
-static int ocfs2_generic_unblock_lock(struct ocfs2_super *osb,
- struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl,
- ocfs2_convert_worker_t *worker);
-
static struct ocfs2_lock_res_ops ocfs2_inode_rw_lops = {
.get_osb = ocfs2_get_inode_osb,
- .unblock = ocfs2_unblock_inode_lock,
.flags = 0,
};
static struct ocfs2_lock_res_ops ocfs2_inode_meta_lops = {
.get_osb = ocfs2_get_inode_osb,
- .unblock = ocfs2_unblock_meta,
+ .check_downconvert = ocfs2_check_meta_downconvert,
+ .set_lvb = ocfs2_set_meta_lvb,
.flags = LOCK_TYPE_REQUIRES_REFRESH|LOCK_TYPE_USES_LVB,
};
static struct ocfs2_lock_res_ops ocfs2_inode_data_lops = {
.get_osb = ocfs2_get_inode_osb,
- .unblock = ocfs2_unblock_data,
+ .downconvert_worker = ocfs2_data_convert_worker,
.flags = 0,
};
static struct ocfs2_lock_res_ops ocfs2_super_lops = {
- .unblock = ocfs2_unblock_osb_lock,
.flags = LOCK_TYPE_REQUIRES_REFRESH,
};
static struct ocfs2_lock_res_ops ocfs2_rename_lops = {
- .unblock = ocfs2_unblock_osb_lock,
.flags = 0,
};
static struct ocfs2_lock_res_ops ocfs2_dentry_lops = {
.get_osb = ocfs2_get_dentry_osb,
- .unblock = ocfs2_unblock_dentry_lock,
.post_unlock = ocfs2_dentry_post_unlock,
+ .downconvert_worker = ocfs2_dentry_convert_worker,
.flags = 0,
};
struct buffer_head **bh);
static void ocfs2_drop_osb_locks(struct ocfs2_super *osb);
static inline int ocfs2_highest_compat_lock_level(int level);
-static inline int ocfs2_can_downconvert_meta_lock(struct inode *inode,
- struct ocfs2_lock_res *lockres,
- int new_level);
static void ocfs2_build_lock_name(enum ocfs2_lock_type type,
u64 blkno,
mlog_exit_void();
}
-typedef void (ocfs2_pre_drop_cb_t)(struct ocfs2_lock_res *, void *);
-
-struct drop_lock_cb {
- ocfs2_pre_drop_cb_t *drop_func;
- void *drop_data;
-};
-
static int ocfs2_drop_lock(struct ocfs2_super *osb,
- struct ocfs2_lock_res *lockres,
- struct drop_lock_cb *dcb)
+ struct ocfs2_lock_res *lockres)
{
enum dlm_status status;
unsigned long flags;
spin_lock_irqsave(&lockres->l_lock, flags);
}
- if (dcb)
- dcb->drop_func(lockres, dcb->drop_data);
+ if (lockres->l_ops->flags & LOCK_TYPE_USES_LVB) {
+ if (lockres->l_flags & OCFS2_LOCK_ATTACHED &&
+ lockres->l_level == LKM_EXMODE &&
+ !(lockres->l_flags & OCFS2_LOCK_NEEDS_REFRESH))
+ lockres->l_ops->set_lvb(lockres);
+ }
if (lockres->l_flags & OCFS2_LOCK_BUSY)
mlog(ML_ERROR, "destroying busy lock: \"%s\"\n",
int ret;
ocfs2_mark_lockres_freeing(lockres);
- ret = ocfs2_drop_lock(osb, lockres, NULL);
+ ret = ocfs2_drop_lock(osb, lockres);
if (ret)
mlog_errno(ret);
}
ocfs2_simple_drop_lockres(osb, &osb->osb_rename_lockres);
}
-static void ocfs2_meta_pre_drop(struct ocfs2_lock_res *lockres, void *data)
-{
- struct inode *inode = data;
-
- /* the metadata lock requires a bit more work as we have an
- * LVB to worry about. */
- if (lockres->l_flags & OCFS2_LOCK_ATTACHED &&
- lockres->l_level == LKM_EXMODE &&
- !(lockres->l_flags & OCFS2_LOCK_NEEDS_REFRESH))
- __ocfs2_stuff_meta_lvb(inode);
-}
-
int ocfs2_drop_inode_locks(struct inode *inode)
{
int status, err;
- struct drop_lock_cb meta_dcb = { ocfs2_meta_pre_drop, inode, };
mlog_entry_void();
* ocfs2_clear_inode has done it for us. */
err = ocfs2_drop_lock(OCFS2_SB(inode->i_sb),
- &OCFS2_I(inode)->ip_data_lockres,
- NULL);
+ &OCFS2_I(inode)->ip_data_lockres);
if (err < 0)
mlog_errno(err);
status = err;
err = ocfs2_drop_lock(OCFS2_SB(inode->i_sb),
- &OCFS2_I(inode)->ip_meta_lockres,
- &meta_dcb);
+ &OCFS2_I(inode)->ip_meta_lockres);
if (err < 0)
mlog_errno(err);
if (err < 0 && !status)
status = err;
err = ocfs2_drop_lock(OCFS2_SB(inode->i_sb),
- &OCFS2_I(inode)->ip_rw_lockres,
- NULL);
+ &OCFS2_I(inode)->ip_rw_lockres);
if (err < 0)
mlog_errno(err);
if (err < 0 && !status)
return ret;
}
-static inline int ocfs2_can_downconvert_meta_lock(struct inode *inode,
- struct ocfs2_lock_res *lockres,
- int new_level)
-{
- int ret;
-
- mlog_entry_void();
-
- BUG_ON(new_level != LKM_NLMODE && new_level != LKM_PRMODE);
-
- if (lockres->l_flags & OCFS2_LOCK_REFRESHING) {
- ret = 0;
- mlog(0, "lockres %s currently being refreshed -- backing "
- "off!\n", lockres->l_name);
- } else if (new_level == LKM_PRMODE)
- ret = !lockres->l_ex_holders &&
- ocfs2_inode_fully_checkpointed(inode);
- else /* Must be NLMODE we're converting to. */
- ret = !lockres->l_ro_holders && !lockres->l_ex_holders &&
- ocfs2_inode_fully_checkpointed(inode);
-
- mlog_exit(ret);
- return ret;
-}
-
-static int ocfs2_do_unblock_meta(struct inode *inode,
- int *requeue)
-{
- int new_level;
- int set_lvb = 0;
- int ret = 0;
- struct ocfs2_lock_res *lockres = &OCFS2_I(inode)->ip_meta_lockres;
- unsigned long flags;
-
- struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
-
- mlog_entry_void();
-
- spin_lock_irqsave(&lockres->l_lock, flags);
-
- BUG_ON(!(lockres->l_flags & OCFS2_LOCK_BLOCKED));
-
- mlog(0, "l_level=%d, l_blocking=%d\n", lockres->l_level,
- lockres->l_blocking);
-
- BUG_ON(lockres->l_level != LKM_EXMODE &&
- lockres->l_level != LKM_PRMODE);
-
- if (lockres->l_flags & OCFS2_LOCK_BUSY) {
- *requeue = 1;
- ret = ocfs2_prepare_cancel_convert(osb, lockres);
- spin_unlock_irqrestore(&lockres->l_lock, flags);
- if (ret) {
- ret = ocfs2_cancel_convert(osb, lockres);
- if (ret < 0)
- mlog_errno(ret);
- }
- goto leave;
- }
-
- new_level = ocfs2_highest_compat_lock_level(lockres->l_blocking);
-
- mlog(0, "l_level=%d, l_blocking=%d, new_level=%d\n",
- lockres->l_level, lockres->l_blocking, new_level);
-
- if (ocfs2_can_downconvert_meta_lock(inode, lockres, new_level)) {
- if (lockres->l_level == LKM_EXMODE)
- set_lvb = 1;
-
- /* If the lock hasn't been refreshed yet (rare), then
- * our memory inode values are old and we skip
- * stuffing the lvb. There's no need to actually clear
- * out the lvb here as it's value is still valid. */
- if (!(lockres->l_flags & OCFS2_LOCK_NEEDS_REFRESH)) {
- if (set_lvb)
- __ocfs2_stuff_meta_lvb(inode);
- } else
- mlog(0, "lockres %s: downconverting stale lock!\n",
- lockres->l_name);
-
- mlog(0, "calling ocfs2_downconvert_lock with l_level=%d, "
- "l_blocking=%d, new_level=%d\n",
- lockres->l_level, lockres->l_blocking, new_level);
-
- ocfs2_prepare_downconvert(lockres, new_level);
- spin_unlock_irqrestore(&lockres->l_lock, flags);
- ret = ocfs2_downconvert_lock(osb, lockres, new_level, set_lvb);
- goto leave;
- }
- if (!ocfs2_inode_fully_checkpointed(inode))
- ocfs2_start_checkpoint(osb);
-
- *requeue = 1;
- spin_unlock_irqrestore(&lockres->l_lock, flags);
- ret = 0;
-leave:
- mlog_exit(ret);
- return ret;
-}
-
-static int ocfs2_generic_unblock_lock(struct ocfs2_super *osb,
- struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl,
- ocfs2_convert_worker_t *worker)
+static int ocfs2_unblock_lock(struct ocfs2_super *osb,
+ struct ocfs2_lock_res *lockres,
+ struct ocfs2_unblock_ctl *ctl)
{
unsigned long flags;
int blocking;
/* If we get here, then we know that there are no more
* incompatible holders (and anyone asking for an incompatible
* lock is blocked). We can now downconvert the lock */
- if (!worker)
+ if (!lockres->l_ops->downconvert_worker)
goto downconvert;
/* Some lockres types want to do a bit of work before
blocking = lockres->l_blocking;
spin_unlock_irqrestore(&lockres->l_lock, flags);
- ctl->unblock_action = worker(lockres, blocking);
+ ctl->unblock_action = lockres->l_ops->downconvert_worker(lockres, blocking);
if (ctl->unblock_action == UNBLOCK_STOP_POST)
goto leave;
return UNBLOCK_CONTINUE;
}
-int ocfs2_unblock_data(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl)
+static int ocfs2_check_meta_downconvert(struct ocfs2_lock_res *lockres,
+ int new_level)
{
- int status;
- struct inode *inode;
- struct ocfs2_super *osb;
-
- mlog_entry_void();
-
- inode = ocfs2_lock_res_inode(lockres);
- osb = OCFS2_SB(inode->i_sb);
-
- mlog(0, "unblock inode %llu\n",
- (unsigned long long)OCFS2_I(inode)->ip_blkno);
-
- status = ocfs2_generic_unblock_lock(osb, lockres, ctl,
- ocfs2_data_convert_worker);
- if (status < 0)
- mlog_errno(status);
-
- mlog(0, "inode %llu, requeue = %d\n",
- (unsigned long long)OCFS2_I(inode)->ip_blkno, ctl->requeue);
-
- mlog_exit(status);
- return status;
-}
-
-static int ocfs2_unblock_inode_lock(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl)
-{
- int status;
- struct inode *inode;
-
- mlog_entry_void();
-
- mlog(0, "Unblock lockres %s\n", lockres->l_name);
+ struct inode *inode = ocfs2_lock_res_inode(lockres);
+ int checkpointed = ocfs2_inode_fully_checkpointed(inode);
- inode = ocfs2_lock_res_inode(lockres);
+ BUG_ON(new_level != LKM_NLMODE && new_level != LKM_PRMODE);
+ BUG_ON(lockres->l_level != LKM_EXMODE && !checkpointed);
- status = ocfs2_generic_unblock_lock(OCFS2_SB(inode->i_sb),
- lockres, ctl, NULL);
- if (status < 0)
- mlog_errno(status);
+ if (checkpointed)
+ return 1;
- mlog_exit(status);
- return status;
+ ocfs2_start_checkpoint(OCFS2_SB(inode->i_sb));
+ return 0;
}
-static int ocfs2_unblock_meta(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl)
+static void ocfs2_set_meta_lvb(struct ocfs2_lock_res *lockres)
{
- int status;
- struct inode *inode;
-
- mlog_entry_void();
-
- inode = ocfs2_lock_res_inode(lockres);
-
- mlog(0, "unblock inode %llu\n",
- (unsigned long long)OCFS2_I(inode)->ip_blkno);
-
- status = ocfs2_do_unblock_meta(inode, &ctl->requeue);
- if (status < 0)
- mlog_errno(status);
-
- mlog(0, "inode %llu, requeue = %d\n",
- (unsigned long long)OCFS2_I(inode)->ip_blkno, ctl->requeue);
+ struct inode *inode = ocfs2_lock_res_inode(lockres);
- mlog_exit(status);
- return status;
+ __ocfs2_stuff_meta_lvb(inode);
}
/*
return UNBLOCK_CONTINUE_POST;
}
-static int ocfs2_unblock_dentry_lock(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl)
-{
- int ret;
- struct ocfs2_dentry_lock *dl = ocfs2_lock_res_dl(lockres);
- struct ocfs2_super *osb = OCFS2_SB(dl->dl_inode->i_sb);
-
- mlog(0, "unblock dentry lock: %llu\n",
- (unsigned long long)OCFS2_I(dl->dl_inode)->ip_blkno);
-
- ret = ocfs2_generic_unblock_lock(osb,
- lockres,
- ctl,
- ocfs2_dentry_convert_worker);
- if (ret < 0)
- mlog_errno(ret);
-
- mlog(0, "requeue = %d, post = %d\n", ctl->requeue, ctl->unblock_action);
-
- return ret;
-}
-
-/* Generic unblock function for any lockres whose private data is an
- * ocfs2_super pointer. */
-static int ocfs2_unblock_osb_lock(struct ocfs2_lock_res *lockres,
- struct ocfs2_unblock_ctl *ctl)
-{
- int status;
- struct ocfs2_super *osb;
-
- mlog_entry_void();
-
- mlog(0, "Unblock lockres %s\n", lockres->l_name);
-
- osb = ocfs2_get_lockres_osb(lockres);
-
- status = ocfs2_generic_unblock_lock(osb,
- lockres,
- ctl,
- NULL);
- if (status < 0)
- mlog_errno(status);
-
- mlog_exit(status);
- return status;
-}
-
void ocfs2_process_blocked_lock(struct ocfs2_super *osb,
struct ocfs2_lock_res *lockres)
{
BUG_ON(!lockres);
BUG_ON(!lockres->l_ops);
- BUG_ON(!lockres->l_ops->unblock);
mlog(0, "lockres %s blocked.\n", lockres->l_name);
goto unqueue;
spin_unlock_irqrestore(&lockres->l_lock, flags);
- status = lockres->l_ops->unblock(lockres, &ctl);
+ status = ocfs2_unblock_lock(osb, lockres, &ctl);
if (status < 0)
mlog_errno(status);