Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/fs/ocfs2/dlm/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 97 kB image not shown  

Quelle  dlmmaster.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * dlmmod.c
 *
 * standalone DLM module
 *
 * Copyright (C) 2004 Oracle.  All rights reserved.
 */



#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/highmem.h>
#include <linux/init.h>
#include <linux/sysctl.h>
#include <linux/random.h>
#include <linux/blkdev.h>
#include <linux/socket.h>
#include <linux/inet.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/string_choices.h>

#include "../cluster/heartbeat.h"
#include "../cluster/nodemanager.h"
#include "../cluster/tcp.h"

#include "dlmapi.h"
#include "dlmcommon.h"
#include "dlmdomain.h"
#include "dlmdebug.h"

#define MLOG_MASK_PREFIX (ML_DLM|ML_DLM_MASTER)
#include "../cluster/masklog.h"

static void dlm_mle_node_down(struct dlm_ctxt *dlm,
         struct dlm_master_list_entry *mle,
         struct o2nm_node *node,
         int idx);
static void dlm_mle_node_up(struct dlm_ctxt *dlm,
       struct dlm_master_list_entry *mle,
       struct o2nm_node *node,
       int idx);

static void dlm_assert_master_worker(struct dlm_work_item *item, void *data);
static int dlm_do_assert_master(struct dlm_ctxt *dlm,
    struct dlm_lock_resource *res,
    void *nodemap, u32 flags);
static void dlm_deref_lockres_worker(struct dlm_work_item *item, void *data);

static inline int dlm_mle_equal(struct dlm_ctxt *dlm,
    struct dlm_master_list_entry *mle,
    const char *name,
    unsigned int namelen)
{
 if (dlm != mle->dlm)
  return 0;

 if (namelen != mle->mnamelen ||
     memcmp(name, mle->mname, namelen) != 0)
  return 0;

 return 1;
}

static struct kmem_cache *dlm_lockres_cache;
static struct kmem_cache *dlm_lockname_cache;
static struct kmem_cache *dlm_mle_cache;

static void dlm_mle_release(struct kref *kref);
static void dlm_init_mle(struct dlm_master_list_entry *mle,
   enum dlm_mle_type type,
   struct dlm_ctxt *dlm,
   struct dlm_lock_resource *res,
   const char *name,
   unsigned int namelen);
static void dlm_put_mle(struct dlm_master_list_entry *mle);
static void __dlm_put_mle(struct dlm_master_list_entry *mle);
static int dlm_find_mle(struct dlm_ctxt *dlm,
   struct dlm_master_list_entry **mle,
   char *name, unsigned int namelen);

static int dlm_do_master_request(struct dlm_lock_resource *res,
     struct dlm_master_list_entry *mle, int to);


static int dlm_wait_for_lock_mastery(struct dlm_ctxt *dlm,
         struct dlm_lock_resource *res,
         struct dlm_master_list_entry *mle,
         int *blocked);
static int dlm_restart_lock_mastery(struct dlm_ctxt *dlm,
        struct dlm_lock_resource *res,
        struct dlm_master_list_entry *mle,
        int blocked);
static int dlm_add_migration_mle(struct dlm_ctxt *dlm,
     struct dlm_lock_resource *res,
     struct dlm_master_list_entry *mle,
     struct dlm_master_list_entry **oldmle,
     const char *name, unsigned int namelen,
     u8 new_master, u8 master);

static u8 dlm_pick_migration_target(struct dlm_ctxt *dlm,
        struct dlm_lock_resource *res);
static void dlm_remove_nonlocal_locks(struct dlm_ctxt *dlm,
          struct dlm_lock_resource *res);
static int dlm_mark_lockres_migrating(struct dlm_ctxt *dlm,
           struct dlm_lock_resource *res,
           u8 target);
static int dlm_pre_master_reco_lockres(struct dlm_ctxt *dlm,
           struct dlm_lock_resource *res);


int dlm_is_host_down(int errno)
{
 switch (errno) {
  case -EBADF:
  case -ECONNREFUSED:
  case -ENOTCONN:
  case -ECONNRESET:
  case -EPIPE:
  case -EHOSTDOWN:
  case -EHOSTUNREACH:
  case -ETIMEDOUT:
  case -ECONNABORTED:
  case -ENETDOWN:
  case -ENETUNREACH:
  case -ENETRESET:
  case -ESHUTDOWN:
  case -ENOPROTOOPT:
  case -EINVAL:   /* if returned from our tcp code,
   this means there is no socket */

   return 1;
 }
 return 0;
}


/*
 * MASTER LIST FUNCTIONS
 */



/*
 * regarding master list entries and heartbeat callbacks:
 *
 * in order to avoid sleeping and allocation that occurs in
 * heartbeat, master list entries are simply attached to the
 * dlm's established heartbeat callbacks.  the mle is attached
 * when it is created, and since the dlm->spinlock is held at
 * that time, any heartbeat event will be properly discovered
 * by the mle.  the mle needs to be detached from the
 * dlm->mle_hb_events list as soon as heartbeat events are no
 * longer useful to the mle, and before the mle is freed.
 *
 * as a general rule, heartbeat events are no longer needed by
 * the mle once an "answer" regarding the lock master has been
 * received.
 */

static inline void __dlm_mle_attach_hb_events(struct dlm_ctxt *dlm,
           struct dlm_master_list_entry *mle)
{
 assert_spin_locked(&dlm->spinlock);

 list_add_tail(&mle->hb_events, &dlm->mle_hb_events);
}


static inline void __dlm_mle_detach_hb_events(struct dlm_ctxt *dlm,
           struct dlm_master_list_entry *mle)
{
 if (!list_empty(&mle->hb_events))
  list_del_init(&mle->hb_events);
}


static inline void dlm_mle_detach_hb_events(struct dlm_ctxt *dlm,
         struct dlm_master_list_entry *mle)
{
 spin_lock(&dlm->spinlock);
 __dlm_mle_detach_hb_events(dlm, mle);
 spin_unlock(&dlm->spinlock);
}

static void dlm_get_mle_inuse(struct dlm_master_list_entry *mle)
{
 struct dlm_ctxt *dlm;
 dlm = mle->dlm;

 assert_spin_locked(&dlm->spinlock);
 assert_spin_locked(&dlm->master_lock);
 mle->inuse++;
 kref_get(&mle->mle_refs);
}

static void dlm_put_mle_inuse(struct dlm_master_list_entry *mle)
{
 struct dlm_ctxt *dlm;
 dlm = mle->dlm;

 spin_lock(&dlm->spinlock);
 spin_lock(&dlm->master_lock);
 mle->inuse--;
 __dlm_put_mle(mle);
 spin_unlock(&dlm->master_lock);
 spin_unlock(&dlm->spinlock);

}

/* remove from list and free */
static void __dlm_put_mle(struct dlm_master_list_entry *mle)
{
 struct dlm_ctxt *dlm;
 dlm = mle->dlm;

 assert_spin_locked(&dlm->spinlock);
 assert_spin_locked(&dlm->master_lock);
 if (!kref_read(&mle->mle_refs)) {
  /* this may or may not crash, but who cares.
 * it's a BUG. */

  mlog(ML_ERROR, "bad mle: %p\n", mle);
  dlm_print_one_mle(mle);
  BUG();
 } else
  kref_put(&mle->mle_refs, dlm_mle_release);
}


/* must not have any spinlocks coming in */
static void dlm_put_mle(struct dlm_master_list_entry *mle)
{
 struct dlm_ctxt *dlm;
 dlm = mle->dlm;

 spin_lock(&dlm->spinlock);
 spin_lock(&dlm->master_lock);
 __dlm_put_mle(mle);
 spin_unlock(&dlm->master_lock);
 spin_unlock(&dlm->spinlock);
}

static inline void dlm_get_mle(struct dlm_master_list_entry *mle)
{
 kref_get(&mle->mle_refs);
}

static void dlm_init_mle(struct dlm_master_list_entry *mle,
   enum dlm_mle_type type,
   struct dlm_ctxt *dlm,
   struct dlm_lock_resource *res,
   const char *name,
   unsigned int namelen)
{
 assert_spin_locked(&dlm->spinlock);

 mle->dlm = dlm;
 mle->type = type;
 INIT_HLIST_NODE(&mle->master_hash_node);
 INIT_LIST_HEAD(&mle->hb_events);
 bitmap_zero(mle->maybe_map, O2NM_MAX_NODES);
 spin_lock_init(&mle->spinlock);
 init_waitqueue_head(&mle->wq);
 atomic_set(&mle->woken, 0);
 kref_init(&mle->mle_refs);
 bitmap_zero(mle->response_map, O2NM_MAX_NODES);
 mle->master = O2NM_MAX_NODES;
 mle->new_master = O2NM_MAX_NODES;
 mle->inuse = 0;

 BUG_ON(mle->type != DLM_MLE_BLOCK &&
        mle->type != DLM_MLE_MASTER &&
        mle->type != DLM_MLE_MIGRATION);

 if (mle->type == DLM_MLE_MASTER) {
  BUG_ON(!res);
  mle->mleres = res;
  memcpy(mle->mname, res->lockname.name, res->lockname.len);
  mle->mnamelen = res->lockname.len;
  mle->mnamehash = res->lockname.hash;
 } else {
  BUG_ON(!name);
  mle->mleres = NULL;
  memcpy(mle->mname, name, namelen);
  mle->mnamelen = namelen;
  mle->mnamehash = dlm_lockid_hash(name, namelen);
 }

 atomic_inc(&dlm->mle_tot_count[mle->type]);
 atomic_inc(&dlm->mle_cur_count[mle->type]);

 /* copy off the node_map and register hb callbacks on our copy */
 bitmap_copy(mle->node_map, dlm->domain_map, O2NM_MAX_NODES);
 bitmap_copy(mle->vote_map, dlm->domain_map, O2NM_MAX_NODES);
 clear_bit(dlm->node_num, mle->vote_map);
 clear_bit(dlm->node_num, mle->node_map);

 /* attach the mle to the domain node up/down events */
 __dlm_mle_attach_hb_events(dlm, mle);
}

void __dlm_unlink_mle(struct dlm_ctxt *dlm, struct dlm_master_list_entry *mle)
{
 assert_spin_locked(&dlm->spinlock);
 assert_spin_locked(&dlm->master_lock);

 if (!hlist_unhashed(&mle->master_hash_node))
  hlist_del_init(&mle->master_hash_node);
}

void __dlm_insert_mle(struct dlm_ctxt *dlm, struct dlm_master_list_entry *mle)
{
 struct hlist_head *bucket;

 assert_spin_locked(&dlm->master_lock);

 bucket = dlm_master_hash(dlm, mle->mnamehash);
 hlist_add_head(&mle->master_hash_node, bucket);
}

/* returns 1 if found, 0 if not */
static int dlm_find_mle(struct dlm_ctxt *dlm,
   struct dlm_master_list_entry **mle,
   char *name, unsigned int namelen)
{
 struct dlm_master_list_entry *tmpmle;
 struct hlist_head *bucket;
 unsigned int hash;

 assert_spin_locked(&dlm->master_lock);

 hash = dlm_lockid_hash(name, namelen);
 bucket = dlm_master_hash(dlm, hash);
 hlist_for_each_entry(tmpmle, bucket, master_hash_node) {
  if (!dlm_mle_equal(dlm, tmpmle, name, namelen))
   continue;
  dlm_get_mle(tmpmle);
  *mle = tmpmle;
  return 1;
 }
 return 0;
}

void dlm_hb_event_notify_attached(struct dlm_ctxt *dlm, int idx, int node_up)
{
 struct dlm_master_list_entry *mle;

 assert_spin_locked(&dlm->spinlock);

 list_for_each_entry(mle, &dlm->mle_hb_events, hb_events) {
  if (node_up)
   dlm_mle_node_up(dlm, mle, NULL, idx);
  else
   dlm_mle_node_down(dlm, mle, NULL, idx);
 }
}

static void dlm_mle_node_down(struct dlm_ctxt *dlm,
         struct dlm_master_list_entry *mle,
         struct o2nm_node *node, int idx)
{
 spin_lock(&mle->spinlock);

 if (!test_bit(idx, mle->node_map))
  mlog(0, "node %u already removed from nodemap!\n", idx);
 else
  clear_bit(idx, mle->node_map);

 spin_unlock(&mle->spinlock);
}

static void dlm_mle_node_up(struct dlm_ctxt *dlm,
       struct dlm_master_list_entry *mle,
       struct o2nm_node *node, int idx)
{
 spin_lock(&mle->spinlock);

 if (test_bit(idx, mle->node_map))
  mlog(0, "node %u already in node map!\n", idx);
 else
  set_bit(idx, mle->node_map);

 spin_unlock(&mle->spinlock);
}


int dlm_init_mle_cache(void)
{
 dlm_mle_cache = kmem_cache_create("o2dlm_mle",
       sizeof(struct dlm_master_list_entry),
       0, SLAB_HWCACHE_ALIGN,
       NULL);
 if (dlm_mle_cache == NULL)
  return -ENOMEM;
 return 0;
}

void dlm_destroy_mle_cache(void)
{
 kmem_cache_destroy(dlm_mle_cache);
}

static void dlm_mle_release(struct kref *kref)
{
 struct dlm_master_list_entry *mle;
 struct dlm_ctxt *dlm;

 mle = container_of(kref, struct dlm_master_list_entry, mle_refs);
 dlm = mle->dlm;

 assert_spin_locked(&dlm->spinlock);
 assert_spin_locked(&dlm->master_lock);

 mlog(0, "Releasing mle for %.*s, type %d\n", mle->mnamelen, mle->mname,
      mle->type);

 /* remove from list if not already */
 __dlm_unlink_mle(dlm, mle);

 /* detach the mle from the domain node up/down events */
 __dlm_mle_detach_hb_events(dlm, mle);

 atomic_dec(&dlm->mle_cur_count[mle->type]);

 /* NOTE: kfree under spinlock here.
 * if this is bad, we can move this to a freelist. */

 kmem_cache_free(dlm_mle_cache, mle);
}


/*
 * LOCK RESOURCE FUNCTIONS
 */


int dlm_init_master_caches(void)
{
 dlm_lockres_cache = kmem_cache_create("o2dlm_lockres",
           sizeof(struct dlm_lock_resource),
           0, SLAB_HWCACHE_ALIGN, NULL);
 if (!dlm_lockres_cache)
  goto bail;

 dlm_lockname_cache = kmem_cache_create("o2dlm_lockname",
            DLM_LOCKID_NAME_MAX, 0,
            SLAB_HWCACHE_ALIGN, NULL);
 if (!dlm_lockname_cache)
  goto bail;

 return 0;
bail:
 dlm_destroy_master_caches();
 return -ENOMEM;
}

void dlm_destroy_master_caches(void)
{
 kmem_cache_destroy(dlm_lockname_cache);
 dlm_lockname_cache = NULL;

 kmem_cache_destroy(dlm_lockres_cache);
 dlm_lockres_cache = NULL;
}

static void dlm_lockres_release(struct kref *kref)
{
 struct dlm_lock_resource *res;
 struct dlm_ctxt *dlm;

 res = container_of(kref, struct dlm_lock_resource, refs);
 dlm = res->dlm;

 /* This should not happen -- all lockres' have a name
 * associated with them at init time. */

 BUG_ON(!res->lockname.name);

 mlog(0, "destroying lockres %.*s\n", res->lockname.len,
      res->lockname.name);

 atomic_dec(&dlm->res_cur_count);

 if (!hlist_unhashed(&res->hash_node) ||
     !list_empty(&res->granted) ||
     !list_empty(&res->converting) ||
     !list_empty(&res->blocked) ||
     !list_empty(&res->dirty) ||
     !list_empty(&res->recovering) ||
     !list_empty(&res->purge)) {
  mlog(ML_ERROR,
       "Going to BUG for resource %.*s."
       " We're on a list! [%c%c%c%c%c%c%c]\n",
       res->lockname.len, res->lockname.name,
       !hlist_unhashed(&res->hash_node) ? 'H' : ' ',
       !list_empty(&res->granted) ? 'G' : ' ',
       !list_empty(&res->converting) ? 'C' : ' ',
       !list_empty(&res->blocked) ? 'B' : ' ',
       !list_empty(&res->dirty) ? 'D' : ' ',
       !list_empty(&res->recovering) ? 'R' : ' ',
       !list_empty(&res->purge) ? 'P' : ' ');

  dlm_print_one_lock_resource(res);
 }

 /* By the time we're ready to blow this guy away, we shouldn't
 * be on any lists. */

 BUG_ON(!hlist_unhashed(&res->hash_node));
 BUG_ON(!list_empty(&res->granted));
 BUG_ON(!list_empty(&res->converting));
 BUG_ON(!list_empty(&res->blocked));
 BUG_ON(!list_empty(&res->dirty));
 BUG_ON(!list_empty(&res->recovering));
 BUG_ON(!list_empty(&res->purge));

 kmem_cache_free(dlm_lockname_cache, (void *)res->lockname.name);

 kmem_cache_free(dlm_lockres_cache, res);
}

void dlm_lockres_put(struct dlm_lock_resource *res)
{
 kref_put(&res->refs, dlm_lockres_release);
}

static void dlm_init_lockres(struct dlm_ctxt *dlm,
        struct dlm_lock_resource *res,
        const char *name, unsigned int namelen)
{
 char *qname;

 /* If we memset here, we lose our reference to the kmalloc'd
 * res->lockname.name, so be sure to init every field
 * correctly! */


 qname = (char *) res->lockname.name;
 memcpy(qname, name, namelen);

 res->lockname.len = namelen;
 res->lockname.hash = dlm_lockid_hash(name, namelen);

 init_waitqueue_head(&res->wq);
 spin_lock_init(&res->spinlock);
 INIT_HLIST_NODE(&res->hash_node);
 INIT_LIST_HEAD(&res->granted);
 INIT_LIST_HEAD(&res->converting);
 INIT_LIST_HEAD(&res->blocked);
 INIT_LIST_HEAD(&res->dirty);
 INIT_LIST_HEAD(&res->recovering);
 INIT_LIST_HEAD(&res->purge);
 INIT_LIST_HEAD(&res->tracking);
 atomic_set(&res->asts_reserved, 0);
 res->migration_pending = 0;
 res->inflight_locks = 0;
 res->inflight_assert_workers = 0;

 res->dlm = dlm;

 kref_init(&res->refs);

 atomic_inc(&dlm->res_tot_count);
 atomic_inc(&dlm->res_cur_count);

 /* just for consistency */
 spin_lock(&res->spinlock);
 dlm_set_lockres_owner(dlm, res, DLM_LOCK_RES_OWNER_UNKNOWN);
 spin_unlock(&res->spinlock);

 res->state = DLM_LOCK_RES_IN_PROGRESS;

 res->last_used = 0;

 spin_lock(&dlm->track_lock);
 list_add_tail(&res->tracking, &dlm->tracking_list);
 spin_unlock(&dlm->track_lock);

 memset(res->lvb, 0, DLM_LVB_LEN);
 bitmap_zero(res->refmap, O2NM_MAX_NODES);
}

struct dlm_lock_resource *dlm_new_lockres(struct dlm_ctxt *dlm,
       const char *name,
       unsigned int namelen)
{
 struct dlm_lock_resource *res = NULL;

 res = kmem_cache_zalloc(dlm_lockres_cache, GFP_NOFS);
 if (!res)
  goto error;

 res->lockname.name = kmem_cache_zalloc(dlm_lockname_cache, GFP_NOFS);
 if (!res->lockname.name)
  goto error;

 dlm_init_lockres(dlm, res, name, namelen);
 return res;

error:
 if (res)
  kmem_cache_free(dlm_lockres_cache, res);
 return NULL;
}

void dlm_lockres_set_refmap_bit(struct dlm_ctxt *dlm,
    struct dlm_lock_resource *res, int bit)
{
 assert_spin_locked(&res->spinlock);

 mlog(0, "res %.*s, set node %u, %ps()\n", res->lockname.len,
      res->lockname.name, bit, __builtin_return_address(0));

 set_bit(bit, res->refmap);
}

void dlm_lockres_clear_refmap_bit(struct dlm_ctxt *dlm,
      struct dlm_lock_resource *res, int bit)
{
 assert_spin_locked(&res->spinlock);

 mlog(0, "res %.*s, clr node %u, %ps()\n", res->lockname.len,
      res->lockname.name, bit, __builtin_return_address(0));

 clear_bit(bit, res->refmap);
}

static void __dlm_lockres_grab_inflight_ref(struct dlm_ctxt *dlm,
       struct dlm_lock_resource *res)
{
 res->inflight_locks++;

 mlog(0, "%s: res %.*s, inflight++: now %u, %ps()\n", dlm->name,
      res->lockname.len, res->lockname.name, res->inflight_locks,
      __builtin_return_address(0));
}

void dlm_lockres_grab_inflight_ref(struct dlm_ctxt *dlm,
       struct dlm_lock_resource *res)
{
 assert_spin_locked(&res->spinlock);
 __dlm_lockres_grab_inflight_ref(dlm, res);
}

void dlm_lockres_drop_inflight_ref(struct dlm_ctxt *dlm,
       struct dlm_lock_resource *res)
{
 assert_spin_locked(&res->spinlock);

 BUG_ON(res->inflight_locks == 0);

 res->inflight_locks--;

 mlog(0, "%s: res %.*s, inflight--: now %u, %ps()\n", dlm->name,
      res->lockname.len, res->lockname.name, res->inflight_locks,
      __builtin_return_address(0));

 wake_up(&res->wq);
}

void __dlm_lockres_grab_inflight_worker(struct dlm_ctxt *dlm,
  struct dlm_lock_resource *res)
{
 assert_spin_locked(&res->spinlock);
 res->inflight_assert_workers++;
 mlog(0, "%s:%.*s: inflight assert worker++: now %u\n",
   dlm->name, res->lockname.len, res->lockname.name,
   res->inflight_assert_workers);
}

static void __dlm_lockres_drop_inflight_worker(struct dlm_ctxt *dlm,
  struct dlm_lock_resource *res)
{
 assert_spin_locked(&res->spinlock);
 BUG_ON(res->inflight_assert_workers == 0);
 res->inflight_assert_workers--;
 mlog(0, "%s:%.*s: inflight assert worker--: now %u\n",
   dlm->name, res->lockname.len, res->lockname.name,
   res->inflight_assert_workers);
}

static void dlm_lockres_drop_inflight_worker(struct dlm_ctxt *dlm,
  struct dlm_lock_resource *res)
{
 spin_lock(&res->spinlock);
 __dlm_lockres_drop_inflight_worker(dlm, res);
 spin_unlock(&res->spinlock);
}

/*
 * lookup a lock resource by name.
 * may already exist in the hashtable.
 * lockid is null terminated
 *
 * if not, allocate enough for the lockres and for
 * the temporary structure used in doing the mastering.
 *
 * also, do a lookup in the dlm->master_list to see
 * if another node has begun mastering the same lock.
 * if so, there should be a block entry in there
 * for this name, and we should *not* attempt to master
 * the lock here.   need to wait around for that node
 * to assert_master (or die).
 *
 */

struct dlm_lock_resource * dlm_get_lock_resource(struct dlm_ctxt *dlm,
       const char *lockid,
       int namelen,
       int flags)
{
 struct dlm_lock_resource *tmpres=NULL, *res=NULL;
 struct dlm_master_list_entry *mle = NULL;
 struct dlm_master_list_entry *alloc_mle = NULL;
 int blocked = 0;
 int ret, nodenum;
 struct dlm_node_iter iter;
 unsigned int hash;
 int tries = 0;
 int bit, wait_on_recovery = 0;

 BUG_ON(!lockid);

 hash = dlm_lockid_hash(lockid, namelen);

 mlog(0, "get lockres %s (len %d)\n", lockid, namelen);

lookup:
 spin_lock(&dlm->spinlock);
 tmpres = __dlm_lookup_lockres_full(dlm, lockid, namelen, hash);
 if (tmpres) {
  spin_unlock(&dlm->spinlock);
  spin_lock(&tmpres->spinlock);

  /*
 * Right after dlm spinlock was released, dlm_thread could have
 * purged the lockres. Check if lockres got unhashed. If so
 * start over.
 */

  if (hlist_unhashed(&tmpres->hash_node)) {
   spin_unlock(&tmpres->spinlock);
   dlm_lockres_put(tmpres);
   tmpres = NULL;
   goto lookup;
  }

  /* Wait on the thread that is mastering the resource */
  if (tmpres->owner == DLM_LOCK_RES_OWNER_UNKNOWN) {
   __dlm_wait_on_lockres(tmpres);
   BUG_ON(tmpres->owner == DLM_LOCK_RES_OWNER_UNKNOWN);
   spin_unlock(&tmpres->spinlock);
   dlm_lockres_put(tmpres);
   tmpres = NULL;
   goto lookup;
  }

  /* Wait on the resource purge to complete before continuing */
  if (tmpres->state & DLM_LOCK_RES_DROPPING_REF) {
   BUG_ON(tmpres->owner == dlm->node_num);
   __dlm_wait_on_lockres_flags(tmpres,
          DLM_LOCK_RES_DROPPING_REF);
   spin_unlock(&tmpres->spinlock);
   dlm_lockres_put(tmpres);
   tmpres = NULL;
   goto lookup;
  }

  /* Grab inflight ref to pin the resource */
  dlm_lockres_grab_inflight_ref(dlm, tmpres);

  spin_unlock(&tmpres->spinlock);
  if (res) {
   spin_lock(&dlm->track_lock);
   if (!list_empty(&res->tracking))
    list_del_init(&res->tracking);
   else
    mlog(ML_ERROR, "Resource %.*s not "
      "on the Tracking list\n",
      res->lockname.len,
      res->lockname.name);
   spin_unlock(&dlm->track_lock);
   dlm_lockres_put(res);
  }
  res = tmpres;
  goto leave;
 }

 if (!res) {
  spin_unlock(&dlm->spinlock);
  mlog(0, "allocating a new resource\n");
  /* nothing found and we need to allocate one. */
  alloc_mle = kmem_cache_alloc(dlm_mle_cache, GFP_NOFS);
  if (!alloc_mle)
   goto leave;
  res = dlm_new_lockres(dlm, lockid, namelen);
  if (!res)
   goto leave;
  goto lookup;
 }

 mlog(0, "no lockres found, allocated our own: %p\n", res);

 if (flags & LKM_LOCAL) {
  /* caller knows it's safe to assume it's not mastered elsewhere
 * DONE!  return right away */

  spin_lock(&res->spinlock);
  dlm_change_lockres_owner(dlm, res, dlm->node_num);
  __dlm_insert_lockres(dlm, res);
  dlm_lockres_grab_inflight_ref(dlm, res);
  spin_unlock(&res->spinlock);
  spin_unlock(&dlm->spinlock);
  /* lockres still marked IN_PROGRESS */
  goto wake_waiters;
 }

 /* check master list to see if another node has started mastering it */
 spin_lock(&dlm->master_lock);

 /* if we found a block, wait for lock to be mastered by another node */
 blocked = dlm_find_mle(dlm, &mle, (char *)lockid, namelen);
 if (blocked) {
  int mig;
  if (mle->type == DLM_MLE_MASTER) {
   mlog(ML_ERROR, "master entry for nonexistent lock!\n");
   BUG();
  }
  mig = (mle->type == DLM_MLE_MIGRATION);
  /* if there is a migration in progress, let the migration
 * finish before continuing.  we can wait for the absence
 * of the MIGRATION mle: either the migrate finished or
 * one of the nodes died and the mle was cleaned up.
 * if there is a BLOCK here, but it already has a master
 * set, we are too late.  the master does not have a ref
 * for us in the refmap.  detach the mle and drop it.
 * either way, go back to the top and start over. */

  if (mig || mle->master != O2NM_MAX_NODES) {
   BUG_ON(mig && mle->master == dlm->node_num);
   /* we arrived too late.  the master does not
 * have a ref for us. retry. */

   mlog(0, "%s:%.*s: late on %s\n",
        dlm->name, namelen, lockid,
        mig ?  "MIGRATION" : "BLOCK");
   spin_unlock(&dlm->master_lock);
   spin_unlock(&dlm->spinlock);

   /* master is known, detach */
   if (!mig)
    dlm_mle_detach_hb_events(dlm, mle);
   dlm_put_mle(mle);
   mle = NULL;
   /* this is lame, but we can't wait on either
 * the mle or lockres waitqueue here */

   if (mig)
    msleep(100);
   goto lookup;
  }
 } else {
  /* go ahead and try to master lock on this node */
  mle = alloc_mle;
  /* make sure this does not get freed below */
  alloc_mle = NULL;
  dlm_init_mle(mle, DLM_MLE_MASTER, dlm, res, NULL, 0);
  set_bit(dlm->node_num, mle->maybe_map);
  __dlm_insert_mle(dlm, mle);

  /* still holding the dlm spinlock, check the recovery map
 * to see if there are any nodes that still need to be
 * considered.  these will not appear in the mle nodemap
 * but they might own this lockres.  wait on them. */

  bit = find_first_bit(dlm->recovery_map, O2NM_MAX_NODES);
  if (bit < O2NM_MAX_NODES) {
   mlog(0, "%s: res %.*s, At least one node (%d) "
        "to recover before lock mastery can begin\n",
        dlm->name, namelen, (char *)lockid, bit);
   wait_on_recovery = 1;
  }
 }

 /* at this point there is either a DLM_MLE_BLOCK or a
 * DLM_MLE_MASTER on the master list, so it's safe to add the
 * lockres to the hashtable.  anyone who finds the lock will
 * still have to wait on the IN_PROGRESS. */


 /* finally add the lockres to its hash bucket */
 __dlm_insert_lockres(dlm, res);

 /* since this lockres is new it doesn't not require the spinlock */
 __dlm_lockres_grab_inflight_ref(dlm, res);

 /* get an extra ref on the mle in case this is a BLOCK
 * if so, the creator of the BLOCK may try to put the last
 * ref at this time in the assert master handler, so we
 * need an extra one to keep from a bad ptr deref. */

 dlm_get_mle_inuse(mle);
 spin_unlock(&dlm->master_lock);
 spin_unlock(&dlm->spinlock);

redo_request:
 while (wait_on_recovery) {
  /* any cluster changes that occurred after dropping the
 * dlm spinlock would be detectable be a change on the mle,
 * so we only need to clear out the recovery map once. */

  if (dlm_is_recovery_lock(lockid, namelen)) {
   mlog(0, "%s: Recovery map is not empty, but must "
        "master $RECOVERY lock now\n", dlm->name);
   if (!dlm_pre_master_reco_lockres(dlm, res))
    wait_on_recovery = 0;
   else {
    mlog(0, "%s: waiting 500ms for heartbeat state "
        "change\n", dlm->name);
    msleep(500);
   }
   continue;
  }

  dlm_kick_recovery_thread(dlm);
  msleep(1000);
  dlm_wait_for_recovery(dlm);

  spin_lock(&dlm->spinlock);
  bit = find_first_bit(dlm->recovery_map, O2NM_MAX_NODES);
  if (bit < O2NM_MAX_NODES) {
   mlog(0, "%s: res %.*s, At least one node (%d) "
        "to recover before lock mastery can begin\n",
        dlm->name, namelen, (char *)lockid, bit);
   wait_on_recovery = 1;
  } else
   wait_on_recovery = 0;
  spin_unlock(&dlm->spinlock);

  if (wait_on_recovery)
   dlm_wait_for_node_recovery(dlm, bit, 10000);
 }

 /* must wait for lock to be mastered elsewhere */
 if (blocked)
  goto wait;

 ret = -EINVAL;
 dlm_node_iter_init(mle->vote_map, &iter);
 while ((nodenum = dlm_node_iter_next(&iter)) >= 0) {
  ret = dlm_do_master_request(res, mle, nodenum);
  if (ret < 0)
   mlog_errno(ret);
  if (mle->master != O2NM_MAX_NODES) {
   /* found a master ! */
   if (mle->master <= nodenum)
    break;
   /* if our master request has not reached the master
 * yet, keep going until it does.  this is how the
 * master will know that asserts are needed back to
 * the lower nodes. */

   mlog(0, "%s: res %.*s, Requests only up to %u but "
        "master is %u, keep going\n", dlm->name, namelen,
        lockid, nodenum, mle->master);
  }
 }

wait:
 /* keep going until the response map includes all nodes */
 ret = dlm_wait_for_lock_mastery(dlm, res, mle, &blocked);
 if (ret < 0) {
  wait_on_recovery = 1;
  mlog(0, "%s: res %.*s, Node map changed, redo the master "
       "request now, blocked=%d\n", dlm->name, res->lockname.len,
       res->lockname.name, blocked);
  if (++tries > 20) {
   mlog(ML_ERROR, "%s: res %.*s, Spinning on "
        "dlm_wait_for_lock_mastery, blocked = %d\n",
        dlm->name, res->lockname.len,
        res->lockname.name, blocked);
   dlm_print_one_lock_resource(res);
   dlm_print_one_mle(mle);
   tries = 0;
  }
  goto redo_request;
 }

 mlog(0, "%s: res %.*s, Mastered by %u\n", dlm->name, res->lockname.len,
      res->lockname.name, res->owner);
 /* make sure we never continue without this */
 BUG_ON(res->owner == O2NM_MAX_NODES);

 /* master is known, detach if not already detached */
 dlm_mle_detach_hb_events(dlm, mle);
 dlm_put_mle(mle);
 /* put the extra ref */
 dlm_put_mle_inuse(mle);

wake_waiters:
 spin_lock(&res->spinlock);
 res->state &= ~DLM_LOCK_RES_IN_PROGRESS;
 spin_unlock(&res->spinlock);
 wake_up(&res->wq);

leave:
 /* need to free the unused mle */
 if (alloc_mle)
  kmem_cache_free(dlm_mle_cache, alloc_mle);

 return res;
}


#define DLM_MASTERY_TIMEOUT_MS   5000

static int dlm_wait_for_lock_mastery(struct dlm_ctxt *dlm,
         struct dlm_lock_resource *res,
         struct dlm_master_list_entry *mle,
         int *blocked)
{
 u8 m;
 int ret, bit;
 int map_changed, voting_done;
 int assert, sleep;

recheck:
 ret = 0;
 assert = 0;

 /* check if another node has already become the owner */
 spin_lock(&res->spinlock);
 if (res->owner != DLM_LOCK_RES_OWNER_UNKNOWN) {
  mlog(0, "%s:%.*s: owner is suddenly %u\n", dlm->name,
       res->lockname.len, res->lockname.name, res->owner);
  spin_unlock(&res->spinlock);
  /* this will cause the master to re-assert across
 * the whole cluster, freeing up mles */

  if (res->owner != dlm->node_num) {
   ret = dlm_do_master_request(res, mle, res->owner);
   if (ret < 0) {
    /* give recovery a chance to run */
    mlog(ML_ERROR, "link to %u went down?: %d\n", res->owner, ret);
    msleep(500);
    goto recheck;
   }
  }
  ret = 0;
  goto leave;
 }
 spin_unlock(&res->spinlock);

 spin_lock(&mle->spinlock);
 m = mle->master;
 map_changed = !bitmap_equal(mle->vote_map, mle->node_map,
        O2NM_MAX_NODES);
 voting_done = bitmap_equal(mle->vote_map, mle->response_map,
       O2NM_MAX_NODES);

 /* restart if we hit any errors */
 if (map_changed) {
  int b;
  mlog(0, "%s: %.*s: node map changed, restarting\n",
       dlm->name, res->lockname.len, res->lockname.name);
  ret = dlm_restart_lock_mastery(dlm, res, mle, *blocked);
  b = (mle->type == DLM_MLE_BLOCK);
  if ((*blocked && !b) || (!*blocked && b)) {
   mlog(0, "%s:%.*s: status change: old=%d new=%d\n",
        dlm->name, res->lockname.len, res->lockname.name,
        *blocked, b);
   *blocked = b;
  }
  spin_unlock(&mle->spinlock);
  if (ret < 0) {
   mlog_errno(ret);
   goto leave;
  }
  mlog(0, "%s:%.*s: restart lock mastery succeeded, "
       "rechecking now\n", dlm->name, res->lockname.len,
       res->lockname.name);
  goto recheck;
 } else {
  if (!voting_done) {
   mlog(0, "map not changed and voting not done "
        "for %s:%.*s\n", dlm->name, res->lockname.len,
        res->lockname.name);
  }
 }

 if (m != O2NM_MAX_NODES) {
  /* another node has done an assert!
 * all done! */

  sleep = 0;
 } else {
  sleep = 1;
  /* have all nodes responded? */
  if (voting_done && !*blocked) {
   bit = find_first_bit(mle->maybe_map, O2NM_MAX_NODES);
   if (dlm->node_num <= bit) {
    /* my node number is lowest.
   * now tell other nodes that I am
 * mastering this. */

    mle->master = dlm->node_num;
    /* ref was grabbed in get_lock_resource
 * will be dropped in dlmlock_master */

    assert = 1;
    sleep = 0;
   }
   /* if voting is done, but we have not received
 * an assert master yet, we must sleep */

  }
 }

 spin_unlock(&mle->spinlock);

 /* sleep if we haven't finished voting yet */
 if (sleep) {
  unsigned long timeo = msecs_to_jiffies(DLM_MASTERY_TIMEOUT_MS);
  atomic_set(&mle->woken, 0);
  (void)wait_event_timeout(mle->wq,
      (atomic_read(&mle->woken) == 1),
      timeo);
  if (res->owner == O2NM_MAX_NODES) {
   mlog(0, "%s:%.*s: waiting again\n", dlm->name,
        res->lockname.len, res->lockname.name);
   goto recheck;
  }
  mlog(0, "done waiting, master is %u\n", res->owner);
  ret = 0;
  goto leave;
 }

 ret = 0;   /* done */
 if (assert) {
  m = dlm->node_num;
  mlog(0, "about to master %.*s here, this=%u\n",
       res->lockname.len, res->lockname.name, m);
  ret = dlm_do_assert_master(dlm, res, mle->vote_map, 0);
  if (ret) {
   /* This is a failure in the network path,
 * not in the response to the assert_master
 * (any nonzero response is a BUG on this node).
 * Most likely a socket just got disconnected
 * due to node death. */

   mlog_errno(ret);
  }
  /* no longer need to restart lock mastery.
 * all living nodes have been contacted. */

  ret = 0;
 }

 /* set the lockres owner */
 spin_lock(&res->spinlock);
 /* mastery reference obtained either during
 * assert_master_handler or in get_lock_resource */

 dlm_change_lockres_owner(dlm, res, m);
 spin_unlock(&res->spinlock);

leave:
 return ret;
}

struct dlm_bitmap_diff_iter
{
 int curnode;
 unsigned long *orig_bm;
 unsigned long *cur_bm;
 unsigned long diff_bm[BITS_TO_LONGS(O2NM_MAX_NODES)];
};

enum dlm_node_state_change
{
 NODE_DOWN = -1,
 NODE_NO_CHANGE = 0,
 NODE_UP
};

static void dlm_bitmap_diff_iter_init(struct dlm_bitmap_diff_iter *iter,
          unsigned long *orig_bm,
          unsigned long *cur_bm)
{
 unsigned long p1, p2;
 int i;

 iter->curnode = -1;
 iter->orig_bm = orig_bm;
 iter->cur_bm = cur_bm;

 for (i = 0; i < BITS_TO_LONGS(O2NM_MAX_NODES); i++) {
         p1 = *(iter->orig_bm + i);
         p2 = *(iter->cur_bm + i);
  iter->diff_bm[i] = (p1 & ~p2) | (p2 & ~p1);
 }
}

static int dlm_bitmap_diff_iter_next(struct dlm_bitmap_diff_iter *iter,
         enum dlm_node_state_change *state)
{
 int bit;

 if (iter->curnode >= O2NM_MAX_NODES)
  return -ENOENT;

 bit = find_next_bit(iter->diff_bm, O2NM_MAX_NODES,
       iter->curnode+1);
 if (bit >= O2NM_MAX_NODES) {
  iter->curnode = O2NM_MAX_NODES;
  return -ENOENT;
 }

 /* if it was there in the original then this node died */
 if (test_bit(bit, iter->orig_bm))
  *state = NODE_DOWN;
 else
  *state = NODE_UP;

 iter->curnode = bit;
 return bit;
}


static int dlm_restart_lock_mastery(struct dlm_ctxt *dlm,
        struct dlm_lock_resource *res,
        struct dlm_master_list_entry *mle,
        int blocked)
{
 struct dlm_bitmap_diff_iter bdi;
 enum dlm_node_state_change sc;
 int node;
 int ret = 0;

 mlog(0, "something happened such that the "
      "master process may need to be restarted!\n");

 assert_spin_locked(&mle->spinlock);

 dlm_bitmap_diff_iter_init(&bdi, mle->vote_map, mle->node_map);
 node = dlm_bitmap_diff_iter_next(&bdi, &sc);
 while (node >= 0) {
  if (sc == NODE_UP) {
   /* a node came up.  clear any old vote from
 * the response map and set it in the vote map
 * then restart the mastery. */

   mlog(ML_NOTICE, "node %d up while restarting\n", node);

   /* redo the master request, but only for the new node */
   mlog(0, "sending request to new node\n");
   clear_bit(node, mle->response_map);
   set_bit(node, mle->vote_map);
  } else {
   mlog(ML_ERROR, "node down! %d\n", node);
   if (blocked) {
    int lowest = find_first_bit(mle->maybe_map,
             O2NM_MAX_NODES);

    /* act like it was never there */
    clear_bit(node, mle->maybe_map);

           if (node == lowest) {
     mlog(0, "expected master %u died"
         " while this node was blocked "
         "waiting on it!\n", node);
     lowest = find_next_bit(mle->maybe_map,
              O2NM_MAX_NODES,
              lowest+1);
     if (lowest < O2NM_MAX_NODES) {
      mlog(0, "%s:%.*s:still "
           "blocked. waiting on %u "
           "now\n", dlm->name,
           res->lockname.len,
           res->lockname.name,
           lowest);
     } else {
      /* mle is an MLE_BLOCK, but
 * there is now nothing left to
 * block on.  we need to return
 * all the way back out and try
 * again with an MLE_MASTER.
 * dlm_do_local_recovery_cleanup
 * has already run, so the mle
 * refcount is ok */

      mlog(0, "%s:%.*s: no "
           "longer blocking. try to "
           "master this here\n",
           dlm->name,
           res->lockname.len,
           res->lockname.name);
      mle->type = DLM_MLE_MASTER;
      mle->mleres = res;
     }
    }
   }

   /* now blank out everything, as if we had never
 * contacted anyone */

   bitmap_zero(mle->maybe_map, O2NM_MAX_NODES);
   bitmap_zero(mle->response_map, O2NM_MAX_NODES);
   /* reset the vote_map to the current node_map */
   bitmap_copy(mle->vote_map, mle->node_map,
        O2NM_MAX_NODES);
   /* put myself into the maybe map */
   if (mle->type != DLM_MLE_BLOCK)
    set_bit(dlm->node_num, mle->maybe_map);
  }
  ret = -EAGAIN;
  node = dlm_bitmap_diff_iter_next(&bdi, &sc);
 }
 return ret;
}


/*
 * DLM_MASTER_REQUEST_MSG
 *
 * returns: 0 on success,
 *          -errno on a network error
 *
 * on error, the caller should assume the target node is "dead"
 *
 */


static int dlm_do_master_request(struct dlm_lock_resource *res,
     struct dlm_master_list_entry *mle, int to)
{
 struct dlm_ctxt *dlm = mle->dlm;
 struct dlm_master_request request;
 int ret, response=0, resend;

 memset(&request, 0, sizeof(request));
 request.node_idx = dlm->node_num;

 BUG_ON(mle->type == DLM_MLE_MIGRATION);

 request.namelen = (u8)mle->mnamelen;
 memcpy(request.name, mle->mname, request.namelen);

again:
 ret = o2net_send_message(DLM_MASTER_REQUEST_MSG, dlm->key, &request,
     sizeof(request), to, &response);
 if (ret < 0)  {
  if (ret == -ESRCH) {
   /* should never happen */
   mlog(ML_ERROR, "TCP stack not ready!\n");
   BUG();
  } else if (ret == -EINVAL) {
   mlog(ML_ERROR, "bad args passed to o2net!\n");
   BUG();
  } else if (ret == -ENOMEM) {
   mlog(ML_ERROR, "out of memory while trying to send "
        "network message! retrying\n");
   /* this is totally crude */
   msleep(50);
   goto again;
  } else if (!dlm_is_host_down(ret)) {
   /* not a network error. bad. */
   mlog_errno(ret);
   mlog(ML_ERROR, "unhandled error!");
   BUG();
  }
  /* all other errors should be network errors,
 * and likely indicate node death */

  mlog(ML_ERROR, "link to %d went down!\n", to);
  goto out;
 }

 ret = 0;
 resend = 0;
 spin_lock(&mle->spinlock);
 switch (response) {
  case DLM_MASTER_RESP_YES:
   set_bit(to, mle->response_map);
   mlog(0, "node %u is the master, response=YES\n", to);
   mlog(0, "%s:%.*s: master node %u now knows I have a "
        "reference\n", dlm->name, res->lockname.len,
        res->lockname.name, to);
   mle->master = to;
   break;
  case DLM_MASTER_RESP_NO:
   mlog(0, "node %u not master, response=NO\n", to);
   set_bit(to, mle->response_map);
   break;
  case DLM_MASTER_RESP_MAYBE:
   mlog(0, "node %u not master, response=MAYBE\n", to);
   set_bit(to, mle->response_map);
   set_bit(to, mle->maybe_map);
   break;
  case DLM_MASTER_RESP_ERROR:
   mlog(0, "node %u hit an error, resending\n", to);
   resend = 1;
   response = 0;
   break;
  default:
   mlog(ML_ERROR, "bad response! %u\n", response);
   BUG();
 }
 spin_unlock(&mle->spinlock);
 if (resend) {
  /* this is also totally crude */
  msleep(50);
  goto again;
 }

out:
 return ret;
}

/*
 * locks that can be taken here:
 * dlm->spinlock
 * res->spinlock
 * mle->spinlock
 * dlm->master_list
 *
 * if possible, TRIM THIS DOWN!!!
 */

int dlm_master_request_handler(struct o2net_msg *msg, u32 len, void *data,
          void **ret_data)
{
 u8 response = DLM_MASTER_RESP_MAYBE;
 struct dlm_ctxt *dlm = data;
 struct dlm_lock_resource *res = NULL;
 struct dlm_master_request *request = (struct dlm_master_request *) msg->buf;
 struct dlm_master_list_entry *mle = NULL, *tmpmle = NULL;
 char *name;
 unsigned int namelen, hash;
 int found, ret;
 int set_maybe;
 int dispatch_assert = 0;
 int dispatched = 0;

 if (!dlm_grab(dlm))
  return DLM_MASTER_RESP_NO;

 if (!dlm_domain_fully_joined(dlm)) {
  response = DLM_MASTER_RESP_NO;
  goto send_response;
 }

 name = request->name;
 namelen = request->namelen;
 hash = dlm_lockid_hash(name, namelen);

 if (namelen > DLM_LOCKID_NAME_MAX) {
  response = DLM_IVBUFLEN;
  goto send_response;
 }

way_up_top:
 spin_lock(&dlm->spinlock);
 res = __dlm_lookup_lockres(dlm, name, namelen, hash);
 if (res) {
  spin_unlock(&dlm->spinlock);

  /* take care of the easy cases up front */
  spin_lock(&res->spinlock);

  /*
 * Right after dlm spinlock was released, dlm_thread could have
 * purged the lockres. Check if lockres got unhashed. If so
 * start over.
 */

  if (hlist_unhashed(&res->hash_node)) {
   spin_unlock(&res->spinlock);
   dlm_lockres_put(res);
   goto way_up_top;
  }

  if (res->state & (DLM_LOCK_RES_RECOVERING|
      DLM_LOCK_RES_MIGRATING)) {
   spin_unlock(&res->spinlock);
   mlog(0, "returning DLM_MASTER_RESP_ERROR since res is "
        "being recovered/migrated\n");
   response = DLM_MASTER_RESP_ERROR;
   if (mle)
    kmem_cache_free(dlm_mle_cache, mle);
   goto send_response;
  }

  if (res->owner == dlm->node_num) {
   dlm_lockres_set_refmap_bit(dlm, res, request->node_idx);
   spin_unlock(&res->spinlock);
   response = DLM_MASTER_RESP_YES;
   if (mle)
    kmem_cache_free(dlm_mle_cache, mle);

   /* this node is the owner.
 * there is some extra work that needs to
 * happen now.  the requesting node has
 * caused all nodes up to this one to
 * create mles.  this node now needs to
 * go back and clean those up. */

   dispatch_assert = 1;
   goto send_response;
  } else if (res->owner != DLM_LOCK_RES_OWNER_UNKNOWN) {
   spin_unlock(&res->spinlock);
   // mlog(0, "node %u is the master\n", res->owner);
   response = DLM_MASTER_RESP_NO;
   if (mle)
    kmem_cache_free(dlm_mle_cache, mle);
   goto send_response;
  }

  /* ok, there is no owner.  either this node is
 * being blocked, or it is actively trying to
 * master this lock. */

  if (!(res->state & DLM_LOCK_RES_IN_PROGRESS)) {
   mlog(ML_ERROR, "lock with no owner should be "
        "in-progress!\n");
   BUG();
  }

  // mlog(0, "lockres is in progress...\n");
  spin_lock(&dlm->master_lock);
  found = dlm_find_mle(dlm, &tmpmle, name, namelen);
  if (!found) {
   mlog(ML_ERROR, "no mle found for this lock!\n");
   BUG();
  }
  set_maybe = 1;
  spin_lock(&tmpmle->spinlock);
  if (tmpmle->type == DLM_MLE_BLOCK) {
   // mlog(0, "this node is waiting for "
   // "lockres to be mastered\n");
   response = DLM_MASTER_RESP_NO;
  } else if (tmpmle->type == DLM_MLE_MIGRATION) {
   mlog(0, "node %u is master, but trying to migrate to "
        "node %u.\n", tmpmle->master, tmpmle->new_master);
   if (tmpmle->master == dlm->node_num) {
    mlog(ML_ERROR, "no owner on lockres, but this "
         "node is trying to migrate it to %u?!\n",
         tmpmle->new_master);
    BUG();
   } else {
    /* the real master can respond on its own */
    response = DLM_MASTER_RESP_NO;
   }
  } else if (tmpmle->master != DLM_LOCK_RES_OWNER_UNKNOWN) {
   set_maybe = 0;
   if (tmpmle->master == dlm->node_num) {
    response = DLM_MASTER_RESP_YES;
    /* this node will be the owner.
 * go back and clean the mles on any
 * other nodes */

    dispatch_assert = 1;
    dlm_lockres_set_refmap_bit(dlm, res,
          request->node_idx);
   } else
    response = DLM_MASTER_RESP_NO;
  } else {
   // mlog(0, "this node is attempting to "
   // "master lockres\n");
   response = DLM_MASTER_RESP_MAYBE;
  }
  if (set_maybe)
   set_bit(request->node_idx, tmpmle->maybe_map);
  spin_unlock(&tmpmle->spinlock);

  spin_unlock(&dlm->master_lock);
  spin_unlock(&res->spinlock);

  /* keep the mle attached to heartbeat events */
  dlm_put_mle(tmpmle);
  if (mle)
   kmem_cache_free(dlm_mle_cache, mle);
  goto send_response;
 }

 /*
 * lockres doesn't exist on this node
 * if there is an MLE_BLOCK, return NO
 * if there is an MLE_MASTER, return MAYBE
 * otherwise, add an MLE_BLOCK, return NO
 */

 spin_lock(&dlm->master_lock);
 found = dlm_find_mle(dlm, &tmpmle, name, namelen);
 if (!found) {
  /* this lockid has never been seen on this node yet */
  // mlog(0, "no mle found\n");
  if (!mle) {
   spin_unlock(&dlm->master_lock);
   spin_unlock(&dlm->spinlock);

   mle = kmem_cache_alloc(dlm_mle_cache, GFP_NOFS);
   if (!mle) {
    response = DLM_MASTER_RESP_ERROR;
    mlog_errno(-ENOMEM);
    goto send_response;
   }
   goto way_up_top;
  }

  // mlog(0, "this is second time thru, already allocated, "
  // "add the block.\n");
  dlm_init_mle(mle, DLM_MLE_BLOCK, dlm, NULL, name, namelen);
  set_bit(request->node_idx, mle->maybe_map);
  __dlm_insert_mle(dlm, mle);
  response = DLM_MASTER_RESP_NO;
 } else {
  spin_lock(&tmpmle->spinlock);
  if (tmpmle->master == dlm->node_num) {
   mlog(ML_ERROR, "no lockres, but an mle with this node as master!\n");
   BUG();
  }
  if (tmpmle->type == DLM_MLE_BLOCK)
   response = DLM_MASTER_RESP_NO;
  else if (tmpmle->type == DLM_MLE_MIGRATION) {
   mlog(0, "migration mle was found (%u->%u)\n",
        tmpmle->master, tmpmle->new_master);
   /* real master can respond on its own */
   response = DLM_MASTER_RESP_NO;
  } else
   response = DLM_MASTER_RESP_MAYBE;
  set_bit(request->node_idx, tmpmle->maybe_map);
  spin_unlock(&tmpmle->spinlock);
 }
 spin_unlock(&dlm->master_lock);
 spin_unlock(&dlm->spinlock);

 if (found) {
  /* keep the mle attached to heartbeat events */
  dlm_put_mle(tmpmle);
 }
send_response:
 /*
 * __dlm_lookup_lockres() grabbed a reference to this lockres.
 * The reference is released by dlm_assert_master_worker() under
 * the call to dlm_dispatch_assert_master().  If
 * dlm_assert_master_worker() isn't called, we drop it here.
 */

 if (dispatch_assert) {
  mlog(0, "%u is the owner of %.*s, cleaning everyone else\n",
        dlm->node_num, res->lockname.len, res->lockname.name);
  spin_lock(&res->spinlock);
  ret = dlm_dispatch_assert_master(dlm, res, 0, request->node_idx,
       DLM_ASSERT_MASTER_MLE_CLEANUP);
  if (ret < 0) {
   mlog(ML_ERROR, "failed to dispatch assert master work\n");
   response = DLM_MASTER_RESP_ERROR;
   spin_unlock(&res->spinlock);
   dlm_lockres_put(res);
  } else {
   dispatched = 1;
   __dlm_lockres_grab_inflight_worker(dlm, res);
   spin_unlock(&res->spinlock);
  }
 } else {
  if (res)
   dlm_lockres_put(res);
 }

 if (!dispatched)
  dlm_put(dlm);
 return response;
}

/*
 * DLM_ASSERT_MASTER_MSG
 */



/*
 * NOTE: this can be used for debugging
 * can periodically run all locks owned by this node
 * and re-assert across the cluster...
 */

static int dlm_do_assert_master(struct dlm_ctxt *dlm,
    struct dlm_lock_resource *res,
    void *nodemap, u32 flags)
{
 struct dlm_assert_master assert;
 int to, tmpret;
 struct dlm_node_iter iter;
 int ret = 0;
 int reassert;
 const char *lockname = res->lockname.name;
 unsigned int namelen = res->lockname.len;

 BUG_ON(namelen > O2NM_MAX_NAME_LEN);

 spin_lock(&res->spinlock);
 res->state |= DLM_LOCK_RES_SETREF_INPROG;
 spin_unlock(&res->spinlock);

again:
 reassert = 0;

 /* note that if this nodemap is empty, it returns 0 */
 dlm_node_iter_init(nodemap, &iter);
 while ((to = dlm_node_iter_next(&iter)) >= 0) {
  int r = 0;
  struct dlm_master_list_entry *mle = NULL;

  mlog(0, "sending assert master to %d (%.*s)\n", to,
       namelen, lockname);
  memset(&assert, 0, sizeof(assert));
  assert.node_idx = dlm->node_num;
  assert.namelen = namelen;
  memcpy(assert.name, lockname, namelen);
  assert.flags = cpu_to_be32(flags);

  tmpret = o2net_send_message(DLM_ASSERT_MASTER_MSG, dlm->key,
         &assert, sizeof(assert), to, &r);
  if (tmpret < 0) {
   mlog(ML_ERROR, "Error %d when sending message %u (key "
        "0x%x) to node %u\n", tmpret,
        DLM_ASSERT_MASTER_MSG, dlm->key, to);
   if (!dlm_is_host_down(tmpret)) {
    mlog(ML_ERROR, "unhandled error=%d!\n", tmpret);
    BUG();
   }
   /* a node died.  finish out the rest of the nodes. */
   mlog(0, "link to %d went down!\n", to);
   /* any nonzero status return will do */
   ret = tmpret;
   r = 0;
  } else if (r < 0) {
   /* ok, something horribly messed.  kill thyself. */
   mlog(ML_ERROR,"during assert master of %.*s to %u, "
        "got %d.\n", namelen, lockname, to, r);
   spin_lock(&dlm->spinlock);
   spin_lock(&dlm->master_lock);
   if (dlm_find_mle(dlm, &mle, (char *)lockname,
      namelen)) {
    dlm_print_one_mle(mle);
    __dlm_put_mle(mle);
   }
   spin_unlock(&dlm->master_lock);
   spin_unlock(&dlm->spinlock);
   BUG();
  }

  if (r & DLM_ASSERT_RESPONSE_REASSERT &&
      !(r & DLM_ASSERT_RESPONSE_MASTERY_REF)) {
    mlog(ML_ERROR, "%.*s: very strange, "
         "master MLE but no lockres on %u\n",
         namelen, lockname, to);
  }

  if (r & DLM_ASSERT_RESPONSE_REASSERT) {
   mlog(0, "%.*s: node %u create mles on other "
        "nodes and requests a re-assert\n",
        namelen, lockname, to);
   reassert = 1;
  }
  if (r & DLM_ASSERT_RESPONSE_MASTERY_REF) {
   mlog(0, "%.*s: node %u has a reference to this "
        "lockres, set the bit in the refmap\n",
        namelen, lockname, to);
   spin_lock(&res->spinlock);
   dlm_lockres_set_refmap_bit(dlm, res, to);
   spin_unlock(&res->spinlock);
  }
 }

 if (reassert)
  goto again;

 spin_lock(&res->spinlock);
 res->state &= ~DLM_LOCK_RES_SETREF_INPROG;
 spin_unlock(&res->spinlock);
 wake_up(&res->wq);

 return ret;
}

/*
 * locks that can be taken here:
 * dlm->spinlock
 * res->spinlock
 * mle->spinlock
 * dlm->master_list
 *
 * if possible, TRIM THIS DOWN!!!
 */

int dlm_assert_master_handler(struct o2net_msg *msg, u32 len, void *data,
         void **ret_data)
{
 struct dlm_ctxt *dlm = data;
 struct dlm_master_list_entry *mle = NULL;
 struct dlm_assert_master *assert = (struct dlm_assert_master *)msg->buf;
 struct dlm_lock_resource *res = NULL;
 char *name;
 unsigned int namelen, hash;
 u32 flags;
 int master_request = 0, have_lockres_ref = 0;
 int ret = 0;

 if (!dlm_grab(dlm))
  return 0;

 name = assert->name;
 namelen = assert->namelen;
 hash = dlm_lockid_hash(name, namelen);
 flags = be32_to_cpu(assert->flags);

 if (namelen > DLM_LOCKID_NAME_MAX) {
  mlog(ML_ERROR, "Invalid name length!");
  goto done;
 }

 spin_lock(&dlm->spinlock);

 if (flags)
  mlog(0, "assert_master with flags: %u\n", flags);

 /* find the MLE */
 spin_lock(&dlm->master_lock);
 if (!dlm_find_mle(dlm, &mle, name, namelen)) {
  /* not an error, could be master just re-asserting */
  mlog(0, "just got an assert_master from %u, but no "
       "MLE for it! (%.*s)\n", assert->node_idx,
       namelen, name);
 } else {
  int bit = find_first_bit(mle->maybe_map, O2NM_MAX_NODES);
  if (bit >= O2NM_MAX_NODES) {
   /* not necessarily an error, though less likely.
 * could be master just re-asserting. */

   mlog(0, "no bits set in the maybe_map, but %u "
        "is asserting! (%.*s)\n", assert->node_idx,
        namelen, name);
  } else if (bit != assert->node_idx) {
   if (flags & DLM_ASSERT_MASTER_MLE_CLEANUP) {
    mlog(0, "master %u was found, %u should "
         "back off\n", assert->node_idx, bit);
   } else {
    /* with the fix for bug 569, a higher node
 * number winning the mastery will respond
 * YES to mastery requests, but this node
 * had no way of knowing.  let it pass. */

    mlog(0, "%u is the lowest node, "
         "%u is asserting. (%.*s) %u must "
         "have begun after %u won.\n", bit,
         assert->node_idx, namelen, name, bit,
         assert->node_idx);
   }
  }
  if (mle->type == DLM_MLE_MIGRATION) {
   if (flags & DLM_ASSERT_MASTER_MLE_CLEANUP) {
    mlog(0, "%s:%.*s: got cleanup assert"
         " from %u for migration\n",
         dlm->name, namelen, name,
         assert->node_idx);
   } else if (!(flags & DLM_ASSERT_MASTER_FINISH_MIGRATION)) {
    mlog(0, "%s:%.*s: got unrelated assert"
         " from %u for migration, ignoring\n",
         dlm->name, namelen, name,
         assert->node_idx);
    __dlm_put_mle(mle);
    spin_unlock(&dlm->master_lock);
    spin_unlock(&dlm->spinlock);
    goto done;
   }
  }
 }
 spin_unlock(&dlm->master_lock);

 /* ok everything checks out with the MLE
 * now check to see if there is a lockres */

 res = __dlm_lookup_lockres(dlm, name, namelen, hash);
 if (res) {
  spin_lock(&res->spinlock);
  if (res->state & DLM_LOCK_RES_RECOVERING)  {
   mlog(ML_ERROR, "%u asserting but %.*s is "
        "RECOVERING!\n", assert->node_idx, namelen, name);
   goto kill;
  }
  if (!mle) {
   if (res->owner != DLM_LOCK_RES_OWNER_UNKNOWN &&
       res->owner != assert->node_idx) {
    mlog(ML_ERROR, "DIE! Mastery assert from %u, "
         "but current owner is %u! (%.*s)\n",
         assert->node_idx, res->owner, namelen,
         name);
    __dlm_print_one_lock_resource(res);
    BUG();
   }
  } else if (mle->type != DLM_MLE_MIGRATION) {
   if (res->owner != DLM_LOCK_RES_OWNER_UNKNOWN) {
    /* owner is just re-asserting */
    if (res->owner == assert->node_idx) {
     mlog(0, "owner %u re-asserting on "
          "lock %.*s\n", assert->node_idx,
          namelen, name);
     goto ok;
    }
    mlog(ML_ERROR, "got assert_master from "
         "node %u, but %u is the owner! "
         "(%.*s)\n", assert->node_idx,
         res->owner, namelen, name);
    goto kill;
   }
   if (!(res->state & DLM_LOCK_RES_IN_PROGRESS)) {
    mlog(ML_ERROR, "got assert from %u, but lock "
         "with no owner should be "
         "in-progress! (%.*s)\n",
         assert->node_idx,
         namelen, name);
    goto kill;
   }
  } else /* mle->type == DLM_MLE_MIGRATION */ {
   /* should only be getting an assert from new master */
   if (assert->node_idx != mle->new_master) {
    mlog(ML_ERROR, "got assert from %u, but "
         "new master is %u, and old master "
         "was %u (%.*s)\n",
         assert->node_idx, mle->new_master,
         mle->master, namelen, name);
    goto kill;
   }

  }
ok:
  spin_unlock(&res->spinlock);
 }

 // mlog(0, "woo!  got an assert_master from node %u!\n",
 //       assert->node_idx);
 if (mle) {
  int extra_ref = 0;
  int nn = -1;
  int rr, err = 0;

  spin_lock(&mle->spinlock);
  if (mle->type == DLM_MLE_BLOCK || mle->type == DLM_MLE_MIGRATION)
   extra_ref = 1;
  else {
   /* MASTER mle: if any bits set in the response map
 * then the calling node needs to re-assert to clear
 * up nodes that this node contacted */

   while ((nn = find_next_bit (mle->response_map, O2NM_MAX_NODES,
          nn+1)) < O2NM_MAX_NODES) {
    if (nn != dlm->node_num && nn != assert->node_idx) {
     master_request = 1;
     break;
    }
   }
  }
  mle->master = assert->node_idx;
  atomic_set(&mle->woken, 1);
  wake_up(&mle->wq);
  spin_unlock(&mle->spinlock);

  if (res) {
   int wake = 0;
   spin_lock(&res->spinlock);
   if (mle->type == DLM_MLE_MIGRATION) {
    mlog(0, "finishing off migration of lockres %.*s, "
          "from %u to %u\n",
            res->lockname.len, res->lockname.name,
            dlm->node_num, mle->new_master);
    res->state &= ~DLM_LOCK_RES_MIGRATING;
    wake = 1;
    dlm_change_lockres_owner(dlm, res, mle->new_master);
    BUG_ON(res->state & DLM_LOCK_RES_DIRTY);
   } else {
    dlm_change_lockres_owner(dlm, res, mle->master);
   }
   spin_unlock(&res->spinlock);
   have_lockres_ref = 1;
   if (wake)
    wake_up(&res->wq);
  }

  /* master is known, detach if not already detached.
 * ensures that only one assert_master call will happen
 * on this mle. */

  spin_lock(&dlm->master_lock);

  rr = kref_read(&mle->mle_refs);
  if (mle->inuse > 0) {
   if (extra_ref && rr < 3)
    err = 1;
   else if (!extra_ref && rr < 2)
    err = 1;
  } else {
   if (extra_ref && rr < 2)
    err = 1;
   else if (!extra_ref && rr < 1)
    err = 1;
  }
  if (err) {
   mlog(ML_ERROR, "%s:%.*s: got assert master from %u "
        "that will mess up this node, refs=%d, extra=%d, "
        "inuse=%d\n", dlm->name, namelen, name,
        assert->node_idx, rr, extra_ref, mle->inuse);
   dlm_print_one_mle(mle);
  }
  __dlm_unlink_mle(dlm, mle);
  __dlm_mle_detach_hb_events(dlm, mle);
  __dlm_put_mle(mle);
  if (extra_ref) {
   /* the assert master message now balances the extra
   * ref given by the master / migration request message.
   * if this is the last put, it will be removed
   * from the list. */

   __dlm_put_mle(mle);
  }
  spin_unlock(&dlm->master_lock);
 } else if (res) {
  if (res->owner != assert->node_idx) {
   mlog(0, "assert_master from %u, but current "
        "owner is %u (%.*s), no mle\n", assert->node_idx,
        res->owner, namelen, name);
  }
 }
 spin_unlock(&dlm->spinlock);

done:
 ret = 0;
 if (res) {
  spin_lock(&res->spinlock);
  res->state |= DLM_LOCK_RES_SETREF_INPROG;
  spin_unlock(&res->spinlock);
  *ret_data = (void *)res;
 }
 dlm_put(dlm);
 if (master_request) {
  mlog(0, "need to tell master to reassert\n");
  /* positive. negative would shoot down the node. */
  ret |= DLM_ASSERT_RESPONSE_REASSERT;
  if (!have_lockres_ref) {
   mlog(ML_ERROR, "strange, got assert from %u, MASTER "
        "mle present here for %s:%.*s, but no lockres!\n",
        assert->node_idx, dlm->name, namelen, name);
  }
 }
 if (have_lockres_ref) {
  /* let the master know we have a reference to the lockres */
  ret |= DLM_ASSERT_RESPONSE_MASTERY_REF;
  mlog(0, "%s:%.*s: got assert from %u, need a ref\n",
       dlm->name, namelen, name, assert->node_idx);
 }
 return ret;

kill:
 /* kill the caller! */
 mlog(ML_ERROR, "Bad message received from another node. Dumping state "
      "and killing the other node now! This node is OK and can continue.\n");
 __dlm_print_one_lock_resource(res);
 spin_unlock(&res->spinlock);
 spin_lock(&dlm->master_lock);
 if (mle)
  __dlm_put_mle(mle);
 spin_unlock(&dlm->master_lock);
 spin_unlock(&dlm->spinlock);
 *ret_data = (void *)res;
 dlm_put(dlm);
 return -EINVAL;
}

void dlm_assert_master_post_handler(int status, void *data, void *ret_data)
{
 struct dlm_lock_resource *res = (struct dlm_lock_resource *)ret_data;

 if (ret_data) {
  spin_lock(&res->spinlock);
  res->state &= ~DLM_LOCK_RES_SETREF_INPROG;
  spin_unlock(&res->spinlock);
  wake_up(&res->wq);
  dlm_lockres_put(res);
 }
 return;
}

int dlm_dispatch_assert_master(struct dlm_ctxt *dlm,
          struct dlm_lock_resource *res,
          int ignore_higher, u8 request_from, u32 flags)
{
 struct dlm_work_item *item;
 item = kzalloc(sizeof(*item), GFP_ATOMIC);
 if (!item)
  return -ENOMEM;


 /* queue up work for dlm_assert_master_worker */
 dlm_init_work_item(dlm, item, dlm_assert_master_worker, NULL);
 item->u.am.lockres = res; /* already have a ref */
 /* can optionally ignore node numbers higher than this node */
 item->u.am.ignore_higher = ignore_higher;
 item->u.am.request_from = request_from;
 item->u.am.flags = flags;

 if (ignore_higher)
  mlog(0, "IGNORE HIGHER: %.*s\n", res->lockname.len,
       res->lockname.name);

 spin_lock(&dlm->work_lock);
 list_add_tail(&item->list, &dlm->work_list);
 spin_unlock(&dlm->work_lock);

 queue_work(dlm->dlm_worker, &dlm->dispatched_work);
 return 0;
}

static void dlm_assert_master_worker(struct dlm_work_item *item, void *data)
{
 struct dlm_ctxt *dlm = data;
 int ret = 0;
 struct dlm_lock_resource *res;
 unsigned long nodemap[BITS_TO_LONGS(O2NM_MAX_NODES)];
 int ignore_higher;
 int bit;
 u8 request_from;
 u32 flags;

 dlm = item->dlm;
 res = item->u.am.lockres;
 ignore_higher = item->u.am.ignore_higher;
 request_from = item->u.am.request_from;
 flags = item->u.am.flags;

 spin_lock(&dlm->spinlock);
 bitmap_copy(nodemap, dlm->domain_map, O2NM_MAX_NODES);
 spin_unlock(&dlm->spinlock);

 clear_bit(dlm->node_num, nodemap);
 if (ignore_higher) {
  /* if is this just to clear up mles for nodes below
 * this node, do not send the message to the original
 * caller or any node number higher than this */

  clear_bit(request_from, nodemap);
  bit = dlm->node_num;
  while (1) {
   bit = find_next_bit(nodemap, O2NM_MAX_NODES,
         bit+1);
          if (bit >= O2NM_MAX_NODES)
    break;
   clear_bit(bit, nodemap);
  }
 }

 /*
 * If we're migrating this lock to someone else, we are no
 * longer allowed to assert out own mastery.  OTOH, we need to
 * prevent migration from starting while we're still asserting
 * our dominance.  The reserved ast delays migration.
 */

 spin_lock(&res->spinlock);
 if (res->state & DLM_LOCK_RES_MIGRATING) {
  mlog(0, "Someone asked us to assert mastery, but we're "
       "in the middle of migration. Skipping assert, "
       "the new master will handle that.\n");
  spin_unlock(&res->spinlock);
  goto put;
 } else
  __dlm_lockres_reserve_ast(res);
 spin_unlock(&res->spinlock);

 /* this call now finishes out the nodemap
 * even if one or more nodes die */

 mlog(0, "worker about to master %.*s here, this=%u\n",
       res->lockname.len, res->lockname.name, dlm->node_num);
 ret = dlm_do_assert_master(dlm, res, nodemap, flags);
 if (ret < 0) {
  /* no need to restart, we are done */
  if (!dlm_is_host_down(ret))
   mlog_errno(ret);
 }

 /* Ok, we've asserted ourselves.  Let's let migration start. */
 dlm_lockres_release_ast(dlm, res);

put:
 dlm_lockres_drop_inflight_worker(dlm, res);

 dlm_lockres_put(res);

 mlog(0, "finished with dlm_assert_master_worker\n");
}

/* SPECIAL CASE for the $RECOVERY lock used by the recovery thread.
 * We cannot wait for node recovery to complete to begin mastering this
 * lockres because this lockres is used to kick off recovery! ;-)
 * So, do a pre-check on all living nodes to see if any of those nodes
 * think that $RECOVERY is currently mastered by a dead node.  If so,
 * we wait a short time to allow that node to get notified by its own
 * heartbeat stack, then check again.  All $RECOVERY lock resources
 * mastered by dead nodes are purged when the heartbeat callback is
 * fired, so we can know for sure that it is safe to continue once
 * the node returns a live node or no node.  */

static int dlm_pre_master_reco_lockres(struct dlm_ctxt *dlm,
           struct dlm_lock_resource *res)
{
 struct dlm_node_iter iter;
 int nodenum;
 int ret = 0;
 u8 master = DLM_LOCK_RES_OWNER_UNKNOWN;

 spin_lock(&dlm->spinlock);
 dlm_node_iter_init(dlm->domain_map, &iter);
 spin_unlock(&dlm->spinlock);

 while ((nodenum = dlm_node_iter_next(&iter)) >= 0) {
  /* do not send to self */
  if (nodenum == dlm->node_num)
   continue;
  ret = dlm_do_master_requery(dlm, res, nodenum, &master);
  if (ret < 0) {
   mlog_errno(ret);
   if (!dlm_is_host_down(ret))
    BUG();
   /* host is down, so answer for that node would be
 * DLM_LOCK_RES_OWNER_UNKNOWN.  continue. */

   ret = 0;
  }

  if (master != DLM_LOCK_RES_OWNER_UNKNOWN) {
   /* check to see if this master is in the recovery map */
   spin_lock(&dlm->spinlock);
   if (test_bit(master, dlm->recovery_map)) {
    mlog(ML_NOTICE, "%s: node %u has not seen "
         "node %u go down yet, and thinks the "
         "dead node is mastering the recovery "
         "lock. must wait.\n", dlm->name,
         nodenum, master);
    ret = -EAGAIN;
   }
   spin_unlock(&dlm->spinlock);
   mlog(0, "%s: reco lock master is %u\n", dlm->name,
        master);
   break;
  }
 }
 return ret;
}

/*
 * DLM_DEREF_LOCKRES_MSG
 */


int dlm_drop_lockres_ref(struct dlm_ctxt *dlm, struct dlm_lock_resource *res)
{
 struct dlm_deref_lockres deref;
 int ret = 0, r;
 const char *lockname;
 unsigned int namelen;

 lockname = res->lockname.name;
 namelen = res->lockname.len;
 BUG_ON(namelen > O2NM_MAX_NAME_LEN);

 memset(&deref, 0, sizeof(deref));
 deref.node_idx = dlm->node_num;
 deref.namelen = namelen;
 memcpy(deref.name, lockname, namelen);

 ret = o2net_send_message(DLM_DEREF_LOCKRES_MSG, dlm->key,
     &deref, sizeof(deref), res->owner, &r);
 if (ret < 0)
  mlog(ML_ERROR, "%s: res %.*s, error %d send DEREF to node %u\n",
       dlm->name, namelen, lockname, ret, res->owner);
 else if (r < 0) {
  /* BAD.  other node says I did not have a ref. */
  mlog(ML_ERROR, "%s: res %.*s, DEREF to node %u got %d\n",
       dlm->name, namelen, lockname, res->owner, r);
  dlm_print_one_lock_resource(res);
  if (r == -ENOMEM)
   BUG();
 } else
  ret = r;

 return ret;
}

int dlm_deref_lockres_handler(struct o2net_msg *msg, u32 len, void *data,
         void **ret_data)
{
 struct dlm_ctxt *dlm = data;
 struct dlm_deref_lockres *deref = (struct dlm_deref_lockres *)msg->buf;
 struct dlm_lock_resource *res = NULL;
 char *name;
 unsigned int namelen;
 int ret = -EINVAL;
 u8 node;
 unsigned int hash;
 struct dlm_work_item *item;
 int cleared = 0;
 int dispatch = 0;

 if (!dlm_grab(dlm))
  return 0;

 name = deref->name;
 namelen = deref->namelen;
 node = deref->node_idx;

 if (namelen > DLM_LOCKID_NAME_MAX) {
  mlog(ML_ERROR, "Invalid name length!");
  goto done;
 }
 if (deref->node_idx >= O2NM_MAX_NODES) {
  mlog(ML_ERROR, "Invalid node number: %u\n", node);
  goto done;
 }

 hash = dlm_lockid_hash(name, namelen);

 spin_lock(&dlm->spinlock);
 res = __dlm_lookup_lockres_full(dlm, name, namelen, hash);
 if (!res) {
  spin_unlock(&dlm->spinlock);
  mlog(ML_ERROR, "%s:%.*s: bad lockres name\n",
       dlm->name, namelen, name);
  goto done;
 }
 spin_unlock(&dlm->spinlock);

 spin_lock(&res->spinlock);
 if (res->state & DLM_LOCK_RES_SETREF_INPROG)
  dispatch = 1;
 else {
  BUG_ON(res->state & DLM_LOCK_RES_DROPPING_REF);
  if (test_bit(node, res->refmap)) {
   dlm_lockres_clear_refmap_bit(dlm, res, node);
   cleared = 1;
  }
 }
 spin_unlock(&res->spinlock);

 if (!dispatch) {
  if (cleared)
   dlm_lockres_calc_usage(dlm, res);
  else {
   mlog(ML_ERROR, "%s:%.*s: node %u trying to drop ref "
        "but it is already dropped!\n", dlm->name,
        res->lockname.len, res->lockname.name, node);
   dlm_print_one_lock_resource(res);
  }
  ret = DLM_DEREF_RESPONSE_DONE;
  goto done;
 }

 item = kzalloc(sizeof(*item), GFP_NOFS);
 if (!item) {
  ret = -ENOMEM;
  mlog_errno(ret);
  goto done;
 }

 dlm_init_work_item(dlm, item, dlm_deref_lockres_worker, NULL);
 item->u.dl.deref_res = res;
 item->u.dl.deref_node = node;

 spin_lock(&dlm->work_lock);
 list_add_tail(&item->list, &dlm->work_list);
 spin_unlock(&dlm->work_lock);

 queue_work(dlm->dlm_worker, &dlm->dispatched_work);
 return DLM_DEREF_RESPONSE_INPROG;

done:
 if (res)
  dlm_lockres_put(res);
 dlm_put(dlm);

 return ret;
}

int dlm_deref_lockres_done_handler(struct o2net_msg *msg, u32 len, void *data,
         void **ret_data)
{
 struct dlm_ctxt *dlm = data;
 struct dlm_deref_lockres_done *deref
   = (struct dlm_deref_lockres_done *)msg->buf;
 struct dlm_lock_resource *res = NULL;
 char *name;
 unsigned int namelen;
 int ret = -EINVAL;
 u8 node;
 unsigned int hash;

 if (!dlm_grab(dlm))
  return 0;

 name = deref->name;
 namelen = deref->namelen;
 node = deref->node_idx;

 if (namelen > DLM_LOCKID_NAME_MAX) {
  mlog(ML_ERROR, "Invalid name length!");
  goto done;
 }
 if (deref->node_idx >= O2NM_MAX_NODES) {
  mlog(ML_ERROR, "Invalid node number: %u\n", node);
  goto done;
 }

 hash = dlm_lockid_hash(name, namelen);

 spin_lock(&dlm->spinlock);
 res = __dlm_lookup_lockres_full(dlm, name, namelen, hash);
 if (!res) {
  spin_unlock(&dlm->spinlock);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=88 G=91

¤ Dauer der Verarbeitung: 0.25 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.