Commit 932d2d1f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull dlm updates from David Teigland:

 - Remove some unnecesary hold/unhold rsb refcounting in cases where an
   existing refcount is known to exist

 - Remove some unnecessary checking for zero nodeids, which should never
   exist, and add some warning if they do

 - Make the slow freeing of structs in release_lockspace() async, run
   from a workqueue

 - Prior rcu freeing allows some further struct lookups to run without a
   lock

 - Use blocking kernel_connect on sockets to avoid EINPROGRESS

* tag 'dlm-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland/linux-dlm:
  dlm: add missing -ENOMEM if alloc_workqueue() fails
  dlm: do synchronized socket connect call
  dlm: move lkb xarray lookup out of lock
  dlm: move dlm_search_rsb_tree() out of lock
  dlm: use RSB_HASHED to avoid lookup twice
  dlm: async freeing of lockspace resources
  dlm: drop kobject release callback handling
  dlm: warn about invalid nodeid comparsions
  dlm: never return invalid nodeid by dlm_our_nodeid()
  dlm: remove unnecessary refcounts
  dlm: cleanup memory allocation helpers
parents 8751b21a 652b0ae6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -928,7 +928,7 @@ int dlm_comm_seq(int nodeid, uint32_t *seq)

int dlm_our_nodeid(void)
{
	return local_comm ? local_comm->nodeid : 0;
	return local_comm->nodeid;
}

/* num 0 is first addr, num 1 is second addr */
+5 −0
Original line number Diff line number Diff line
@@ -295,6 +295,7 @@ struct dlm_lkb {
		void			*lkb_astparam;	/* caller's ast arg */
		struct dlm_user_args	*lkb_ua;
	};
	struct rcu_head		rcu;
};

/*
@@ -660,6 +661,8 @@ struct dlm_ls {
	const struct dlm_lockspace_ops *ls_ops;
	void			*ls_ops_arg;

	struct work_struct	ls_free_work;

	int			ls_namelen;
	char			ls_name[DLM_LOCKSPACE_LEN + 1];
};
@@ -803,6 +806,8 @@ static inline void dlm_set_sbflags_val(struct dlm_lkb *lkb, uint32_t val)
			  __DLM_SBF_MAX_BIT);
}

extern struct workqueue_struct *dlm_wq;

int dlm_plock_init(void);
void dlm_plock_exit(void);

+70 −58
Original line number Diff line number Diff line
@@ -600,7 +600,7 @@ static int get_rsb_struct(struct dlm_ls *ls, const void *name, int len,
{
	struct dlm_rsb *r;

	r = dlm_allocate_rsb(ls);
	r = dlm_allocate_rsb();
	if (!r)
		return -ENOMEM;

@@ -733,11 +733,13 @@ static int find_rsb_dir(struct dlm_ls *ls, const void *name, int len,
	}

 retry:
	error = dlm_search_rsb_tree(&ls->ls_rsbtbl, name, len, &r);
	if (error)
		goto do_new;

	/* check if the rsb is active under read lock - likely path */
	read_lock_bh(&ls->ls_rsbtbl_lock);
	error = dlm_search_rsb_tree(&ls->ls_rsbtbl, name, len, &r);
	if (error) {
	if (!rsb_flag(r, RSB_HASHED)) {
		read_unlock_bh(&ls->ls_rsbtbl_lock);
		goto do_new;
	}
@@ -918,11 +920,13 @@ static int find_rsb_nodir(struct dlm_ls *ls, const void *name, int len,
	int error;

 retry:
	error = dlm_search_rsb_tree(&ls->ls_rsbtbl, name, len, &r);
	if (error)
		goto do_new;

	/* check if the rsb is in active state under read lock - likely path */
	read_lock_bh(&ls->ls_rsbtbl_lock);
	error = dlm_search_rsb_tree(&ls->ls_rsbtbl, name, len, &r);
	if (error) {
	if (!rsb_flag(r, RSB_HASHED)) {
		read_unlock_bh(&ls->ls_rsbtbl_lock);
		goto do_new;
	}
@@ -1151,7 +1155,7 @@ static void __dlm_master_lookup(struct dlm_ls *ls, struct dlm_rsb *r, int our_no
		r->res_dir_nodeid = our_nodeid;
	}

	if (fix_master && dlm_is_removed(ls, r->res_master_nodeid)) {
	if (fix_master && r->res_master_nodeid && dlm_is_removed(ls, r->res_master_nodeid)) {
		/* Recovery uses this function to set a new master when
		 * the previous master failed.  Setting NEW_MASTER will
		 * force dlm_recover_masters to call recover_master on this
@@ -1276,11 +1280,17 @@ static int _dlm_master_lookup(struct dlm_ls *ls, int from_nodeid, const char *na
	}

 retry:
	error = dlm_search_rsb_tree(&ls->ls_rsbtbl, name, len, &r);
	if (error)
		goto not_found;

	/* check if the rsb is active under read lock - likely path */
	read_lock_bh(&ls->ls_rsbtbl_lock);
	error = dlm_search_rsb_tree(&ls->ls_rsbtbl, name, len, &r);
	if (!error) {
	if (!rsb_flag(r, RSB_HASHED)) {
		read_unlock_bh(&ls->ls_rsbtbl_lock);
		goto not_found;
	}

	if (rsb_flag(r, RSB_INACTIVE)) {
		read_unlock_bh(&ls->ls_rsbtbl_lock);
		goto do_inactive;
@@ -1302,17 +1312,13 @@ static int _dlm_master_lookup(struct dlm_ls *ls, int from_nodeid, const char *na
	put_rsb(r);

	return 0;
	} else {
		read_unlock_bh(&ls->ls_rsbtbl_lock);
		goto not_found;
	}

 do_inactive:
	/* unlikely path - relookup under write */
	/* unlikely path - check if still part of ls_rsbtbl */
	write_lock_bh(&ls->ls_rsbtbl_lock);

	error = dlm_search_rsb_tree(&ls->ls_rsbtbl, name, len, &r);
	if (!error) {
	/* see comment in find_rsb_dir */
	if (rsb_flag(r, RSB_HASHED)) {
		if (!rsb_flag(r, RSB_INACTIVE)) {
			write_unlock_bh(&ls->ls_rsbtbl_lock);
			/* something as changed, very unlikely but
@@ -1403,14 +1409,14 @@ void dlm_dump_rsb_name(struct dlm_ls *ls, const char *name, int len)
	struct dlm_rsb *r = NULL;
	int error;

	read_lock_bh(&ls->ls_rsbtbl_lock);
	rcu_read_lock();
	error = dlm_search_rsb_tree(&ls->ls_rsbtbl, name, len, &r);
	if (!error)
		goto out;

	dlm_dump_rsb(r);
 out:
	read_unlock_bh(&ls->ls_rsbtbl_lock);
	rcu_read_unlock();
}

static void deactivate_rsb(struct kref *kref)
@@ -1442,18 +1448,6 @@ static void deactivate_rsb(struct kref *kref)
	}
}

/* See comment for unhold_lkb */

static void unhold_rsb(struct dlm_rsb *r)
{
	int rv;

	/* inactive rsbs are not ref counted */
	WARN_ON(rsb_flag(r, RSB_INACTIVE));
	rv = kref_put(&r->res_ref, deactivate_rsb);
	DLM_ASSERT(!rv, dlm_dump_rsb(r););
}

void free_inactive_rsb(struct dlm_rsb *r)
{
	WARN_ON_ONCE(!rsb_flag(r, RSB_INACTIVE));
@@ -1497,7 +1491,7 @@ static int _create_lkb(struct dlm_ls *ls, struct dlm_lkb **lkb_ret,
	limit.max = end;
	limit.min = start;

	lkb = dlm_allocate_lkb(ls);
	lkb = dlm_allocate_lkb();
	if (!lkb)
		return -ENOMEM;

@@ -1533,11 +1527,21 @@ static int find_lkb(struct dlm_ls *ls, uint32_t lkid, struct dlm_lkb **lkb_ret)
{
	struct dlm_lkb *lkb;

	read_lock_bh(&ls->ls_lkbxa_lock);
	rcu_read_lock();
	lkb = xa_load(&ls->ls_lkbxa, lkid);
	if (lkb)
	if (lkb) {
		/* check if lkb is still part of lkbxa under lkbxa_lock as
		 * the lkb_ref is tight to the lkbxa data structure, see
		 * __put_lkb().
		 */
		read_lock_bh(&ls->ls_lkbxa_lock);
		if (kref_read(&lkb->lkb_ref))
			kref_get(&lkb->lkb_ref);
		else
			lkb = NULL;
		read_unlock_bh(&ls->ls_lkbxa_lock);
	}
	rcu_read_unlock();

	*lkb_ret = lkb;
	return lkb ? 0 : -ENOENT;
@@ -1675,10 +1679,8 @@ static void del_lkb(struct dlm_rsb *r, struct dlm_lkb *lkb)

static void move_lkb(struct dlm_rsb *r, struct dlm_lkb *lkb, int sts)
{
	hold_lkb(lkb);
	del_lkb(r, lkb);
	add_lkb(r, lkb, sts);
	unhold_lkb(lkb);
}

static int msg_reply_type(int mstype)
@@ -4323,16 +4325,27 @@ static void receive_remove(struct dlm_ls *ls, const struct dlm_message *ms)
	memset(name, 0, sizeof(name));
	memcpy(name, ms->m_extra, len);

	write_lock_bh(&ls->ls_rsbtbl_lock);

	rcu_read_lock();
	rv = dlm_search_rsb_tree(&ls->ls_rsbtbl, name, len, &r);
	if (rv) {
		rcu_read_unlock();
		/* should not happen */
		log_error(ls, "%s from %d not found %s", __func__,
			  from_nodeid, name);
		return;
	}

	write_lock_bh(&ls->ls_rsbtbl_lock);
	if (!rsb_flag(r, RSB_HASHED)) {
		rcu_read_unlock();
		write_unlock_bh(&ls->ls_rsbtbl_lock);
		/* should not happen */
		log_error(ls, "%s from %d got removed during removal %s",
			  __func__, from_nodeid, name);
		return;
	}
	/* at this stage the rsb can only being freed here */
	rcu_read_unlock();

	if (!rsb_flag(r, RSB_INACTIVE)) {
		if (r->res_master_nodeid != from_nodeid) {
@@ -5297,7 +5310,7 @@ int dlm_recover_waiters_post(struct dlm_ls *ls)
			case DLM_MSG_LOOKUP:
			case DLM_MSG_REQUEST:
				_request_lock(r, lkb);
				if (is_master(r))
				if (r->res_nodeid != -1 && is_master(r))
					confirm_master(r, 0);
				break;
			case DLM_MSG_CONVERT:
@@ -5409,9 +5422,8 @@ void dlm_recover_purge(struct dlm_ls *ls, const struct list_head *root_list)
		return;

	list_for_each_entry(r, root_list, res_root_list) {
		hold_rsb(r);
		lock_rsb(r);
		if (is_master(r)) {
		if (r->res_nodeid != -1 && is_master(r)) {
			purge_dead_list(ls, r, &r->res_grantqueue,
					nodeid_gone, &lkb_count);
			purge_dead_list(ls, r, &r->res_convertqueue,
@@ -5420,7 +5432,7 @@ void dlm_recover_purge(struct dlm_ls *ls, const struct list_head *root_list)
					nodeid_gone, &lkb_count);
		}
		unlock_rsb(r);
		unhold_rsb(r);

		cond_resched();
	}

+2 −0
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ int dlm_debug_add_lkb_to_waiters(struct dlm_ls *ls, uint32_t lkb_id,

static inline int is_master(struct dlm_rsb *r)
{
	WARN_ON_ONCE(r->res_nodeid == -1);

	return !r->res_nodeid;
}

+48 −49
Original line number Diff line number Diff line
@@ -174,12 +174,6 @@ static ssize_t dlm_attr_store(struct kobject *kobj, struct attribute *attr,
	return a->store ? a->store(ls, buf, len) : len;
}

static void lockspace_kobj_release(struct kobject *k)
{
	struct dlm_ls *ls  = container_of(k, struct dlm_ls, ls_kobj);
	kfree(ls);
}

static const struct sysfs_ops dlm_attr_ops = {
	.show  = dlm_attr_show,
	.store = dlm_attr_store,
@@ -188,7 +182,6 @@ static const struct sysfs_ops dlm_attr_ops = {
static struct kobj_type dlm_ktype = {
	.default_groups = dlm_groups,
	.sysfs_ops     = &dlm_attr_ops,
	.release       = lockspace_kobj_release,
};

static struct kset *dlm_kset;
@@ -322,13 +315,50 @@ static int threads_start(void)
	return error;
}

static int lkb_idr_free(struct dlm_lkb *lkb)
{
	if (lkb->lkb_lvbptr && test_bit(DLM_IFL_MSTCPY_BIT, &lkb->lkb_iflags))
		dlm_free_lvb(lkb->lkb_lvbptr);

	dlm_free_lkb(lkb);
	return 0;
}

static void rhash_free_rsb(void *ptr, void *arg)
{
	struct dlm_rsb *rsb = ptr;

	dlm_free_rsb(rsb);
}

static void free_lockspace(struct work_struct *work)
{
	struct dlm_ls *ls  = container_of(work, struct dlm_ls, ls_free_work);
	struct dlm_lkb *lkb;
	unsigned long id;

	/*
	 * Free all lkb's in xa
	 */
	xa_for_each(&ls->ls_lkbxa, id, lkb) {
		lkb_idr_free(lkb);
	}
	xa_destroy(&ls->ls_lkbxa);

	/*
	 * Free all rsb's on rsbtbl
	 */
	rhashtable_free_and_destroy(&ls->ls_rsbtbl, rhash_free_rsb, NULL);

	kfree(ls);
}

static int new_lockspace(const char *name, const char *cluster,
			 uint32_t flags, int lvblen,
			 const struct dlm_lockspace_ops *ops, void *ops_arg,
			 int *ops_result, dlm_lockspace_t **lockspace)
{
	struct dlm_ls *ls;
	int do_unreg = 0;
	int namelen = strlen(name);
	int error;

@@ -453,6 +483,8 @@ static int new_lockspace(const char *name, const char *cluster,
	spin_lock_init(&ls->ls_cb_lock);
	INIT_LIST_HEAD(&ls->ls_cb_delay);

	INIT_WORK(&ls->ls_free_work, free_lockspace);

	ls->ls_recoverd_task = NULL;
	mutex_init(&ls->ls_recoverd_active);
	spin_lock_init(&ls->ls_recover_lock);
@@ -530,9 +562,6 @@ static int new_lockspace(const char *name, const char *cluster,
	wait_event(ls->ls_recover_lock_wait,
		   test_bit(LSFL_RECOVER_LOCK, &ls->ls_flags));

	/* let kobject handle freeing of ls if there's an error */
	do_unreg = 1;

	ls->ls_kobj.kset = dlm_kset;
	error = kobject_init_and_add(&ls->ls_kobj, &dlm_ktype, NULL,
				     "%s", ls->ls_name);
@@ -580,9 +609,7 @@ static int new_lockspace(const char *name, const char *cluster,
	xa_destroy(&ls->ls_lkbxa);
	rhashtable_destroy(&ls->ls_rsbtbl);
 out_lsfree:
	if (do_unreg)
	kobject_put(&ls->ls_kobj);
	else
	kfree(ls);
 out:
	module_put(THIS_MODULE);
@@ -640,15 +667,6 @@ int dlm_new_user_lockspace(const char *name, const char *cluster,
				   ops_arg, ops_result, lockspace);
}

static int lkb_idr_free(struct dlm_lkb *lkb)
{
	if (lkb->lkb_lvbptr && test_bit(DLM_IFL_MSTCPY_BIT, &lkb->lkb_iflags))
		dlm_free_lvb(lkb->lkb_lvbptr);

	dlm_free_lkb(lkb);
	return 0;
}

/* NOTE: We check the lkbxa here rather than the resource table.
   This is because there may be LKBs queued as ASTs that have been unlinked
   from their RSBs and are pending deletion once the AST has been delivered */
@@ -680,17 +698,8 @@ static int lockspace_busy(struct dlm_ls *ls, int force)
	return rv;
}

static void rhash_free_rsb(void *ptr, void *arg)
{
	struct dlm_rsb *rsb = ptr;

	dlm_free_rsb(rsb);
}

static int release_lockspace(struct dlm_ls *ls, int force)
{
	struct dlm_lkb *lkb;
	unsigned long id;
	int busy, rv;

	busy = lockspace_busy(ls, force);
@@ -743,22 +752,11 @@ static int release_lockspace(struct dlm_ls *ls, int force)

	dlm_delete_debug_file(ls);

	kobject_put(&ls->ls_kobj);

	xa_destroy(&ls->ls_recover_xa);
	kfree(ls->ls_recover_buf);

	/*
	 * Free all lkb's in xa
	 */
	xa_for_each(&ls->ls_lkbxa, id, lkb) {
		lkb_idr_free(lkb);
	}
	xa_destroy(&ls->ls_lkbxa);

	/*
	 * Free all rsb's on rsbtbl
	 */
	rhashtable_free_and_destroy(&ls->ls_rsbtbl, rhash_free_rsb, NULL);

	/*
	 * Free structures on any other lists
	 */
@@ -768,10 +766,11 @@ static int release_lockspace(struct dlm_ls *ls, int force)
	dlm_clear_members(ls);
	dlm_clear_members_gone(ls);
	kfree(ls->ls_node_array);
	log_rinfo(ls, "release_lockspace final free");
	kobject_put(&ls->ls_kobj);
	/* The ls structure will be freed when the kobject is done with */

	log_rinfo(ls, "%s final free", __func__);

	/* delayed free of data structures see free_lockspace() */
	queue_work(dlm_wq, &ls->ls_free_work);
	module_put(THIS_MODULE);
	return 0;
}
Loading