linux/fs/ocfs2/cluster/heartbeat.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2004, 2005 Oracle.  All rights reserved.
 */

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/file.h>
#include <linux/kthread.h>
#include <linux/configfs.h>
#include <linux/random.h>
#include <linux/crc32.h>
#include <linux/time.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/bitmap.h>
#include <linux/ktime.h>
#include "heartbeat.h"
#include "tcp.h"
#include "nodemanager.h"
#include "quorum.h"

#include "masklog.h"


/*
 * The first heartbeat pass had one global thread that would serialize all hb
 * callback calls.  This global serializing sem should only be removed once
 * we've made sure that all callees can deal with being called concurrently
 * from multiple hb region threads.
 */
static DECLARE_RWSEM(o2hb_callback_sem);

/*
 * multiple hb threads are watching multiple regions.  A node is live
 * whenever any of the threads sees activity from the node in its region.
 */
static DEFINE_SPINLOCK(o2hb_live_lock);
static struct list_head o2hb_live_slots[O2NM_MAX_NODES];
static unsigned long o2hb_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)];
static LIST_HEAD(o2hb_node_events);
static DECLARE_WAIT_QUEUE_HEAD(o2hb_steady_queue);

/*
 * In global heartbeat, we maintain a series of region bitmaps.
 * 	- o2hb_region_bitmap allows us to limit the region number to max region.
 * 	- o2hb_live_region_bitmap tracks live regions (seen steady iterations).
 * 	- o2hb_quorum_region_bitmap tracks live regions that have seen all nodes
 * 		heartbeat on it.
 * 	- o2hb_failed_region_bitmap tracks the regions that have seen io timeouts.
 */
static unsigned long o2hb_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)];
static unsigned long o2hb_live_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)];
static unsigned long o2hb_quorum_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)];
static unsigned long o2hb_failed_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)];

#define O2HB_DB_TYPE_LIVENODES
#define O2HB_DB_TYPE_LIVEREGIONS
#define O2HB_DB_TYPE_QUORUMREGIONS
#define O2HB_DB_TYPE_FAILEDREGIONS
#define O2HB_DB_TYPE_REGION_LIVENODES
#define O2HB_DB_TYPE_REGION_NUMBER
#define O2HB_DB_TYPE_REGION_ELAPSED_TIME
#define O2HB_DB_TYPE_REGION_PINNED
struct o2hb_debug_buf {};

static struct o2hb_debug_buf *o2hb_db_livenodes;
static struct o2hb_debug_buf *o2hb_db_liveregions;
static struct o2hb_debug_buf *o2hb_db_quorumregions;
static struct o2hb_debug_buf *o2hb_db_failedregions;

#define O2HB_DEBUG_DIR
#define O2HB_DEBUG_LIVENODES
#define O2HB_DEBUG_LIVEREGIONS
#define O2HB_DEBUG_QUORUMREGIONS
#define O2HB_DEBUG_FAILEDREGIONS
#define O2HB_DEBUG_REGION_NUMBER
#define O2HB_DEBUG_REGION_ELAPSED_TIME
#define O2HB_DEBUG_REGION_PINNED

static struct dentry *o2hb_debug_dir;

static LIST_HEAD(o2hb_all_regions);

static struct o2hb_callback {} o2hb_callbacks[O2HB_NUM_CB];

static struct o2hb_callback *hbcall_from_type(enum o2hb_callback_type type);

enum o2hb_heartbeat_modes {};

static const char *o2hb_heartbeat_mode_desc[O2HB_HEARTBEAT_NUM_MODES] =;

unsigned int o2hb_dead_threshold =;
static unsigned int o2hb_heartbeat_mode =;

/*
 * o2hb_dependent_users tracks the number of registered callbacks that depend
 * on heartbeat. o2net and o2dlm are two entities that register this callback.
 * However only o2dlm depends on the heartbeat. It does not want the heartbeat
 * to stop while a dlm domain is still active.
 */
static unsigned int o2hb_dependent_users;

/*
 * In global heartbeat mode, all regions are pinned if there are one or more
 * dependent users and the quorum region count is <= O2HB_PIN_CUT_OFF. All
 * regions are unpinned if the region count exceeds the cut off or the number
 * of dependent users falls to zero.
 */
#define O2HB_PIN_CUT_OFF

/*
 * In local heartbeat mode, we assume the dlm domain name to be the same as
 * region uuid. This is true for domains created for the file system but not
 * necessarily true for userdlm domains. This is a known limitation.
 *
 * In global heartbeat mode, we pin/unpin all o2hb regions. This solution
 * works for both file system and userdlm domains.
 */
static int o2hb_region_pin(const char *region_uuid);
static void o2hb_region_unpin(const char *region_uuid);

/* Only sets a new threshold if there are no active regions.
 *
 * No locking or otherwise interesting code is required for reading
 * o2hb_dead_threshold as it can't change once regions are active and
 * it's not interesting to anyone until then anyway. */
static void o2hb_dead_threshold_set(unsigned int threshold)
{}

static int o2hb_global_heartbeat_mode_set(unsigned int hb_mode)
{}

struct o2hb_node_event {};

struct o2hb_disk_slot {};

/* each thread owns a region.. when we're asked to tear down the region
 * we ask the thread to stop, who cleans up the region */
struct o2hb_region {};

static inline struct block_device *reg_bdev(struct o2hb_region *reg)
{}

struct o2hb_bio_wait_ctxt {};

#define O2HB_NEGO_TIMEOUT_MS

enum {};

struct o2hb_nego_msg {};

static void o2hb_write_timeout(struct work_struct *work)
{}

static void o2hb_arm_timeout(struct o2hb_region *reg)
{}

static void o2hb_disarm_timeout(struct o2hb_region *reg)
{}

static int o2hb_send_nego_msg(int key, int type, u8 target)
{}

static void o2hb_nego_timeout(struct work_struct *work)
{}

static int o2hb_nego_timeout_handler(struct o2net_msg *msg, u32 len, void *data,
				void **ret_data)
{}

static int o2hb_nego_approve_handler(struct o2net_msg *msg, u32 len, void *data,
				void **ret_data)
{}

static inline void o2hb_bio_wait_init(struct o2hb_bio_wait_ctxt *wc)
{}

/* Used in error paths too */
static inline void o2hb_bio_wait_dec(struct o2hb_bio_wait_ctxt *wc,
				     unsigned int num)
{}

static void o2hb_wait_on_io(struct o2hb_bio_wait_ctxt *wc)
{}

static void o2hb_bio_end_io(struct bio *bio)
{}

/* Setup a Bio to cover I/O against num_slots slots starting at
 * start_slot. */
static struct bio *o2hb_setup_one_bio(struct o2hb_region *reg,
				      struct o2hb_bio_wait_ctxt *wc,
				      unsigned int *current_slot,
				      unsigned int max_slots, blk_opf_t opf)
{}

static int o2hb_read_slots(struct o2hb_region *reg,
			   unsigned int begin_slot,
			   unsigned int max_slots)
{}

static int o2hb_issue_node_write(struct o2hb_region *reg,
				 struct o2hb_bio_wait_ctxt *write_wc)
{}

static u32 o2hb_compute_block_crc_le(struct o2hb_region *reg,
				     struct o2hb_disk_heartbeat_block *hb_block)
{}

static void o2hb_dump_slot(struct o2hb_disk_heartbeat_block *hb_block)
{}

static int o2hb_verify_crc(struct o2hb_region *reg,
			   struct o2hb_disk_heartbeat_block *hb_block)
{}

/*
 * Compare the slot data with what we wrote in the last iteration.
 * If the match fails, print an appropriate error message. This is to
 * detect errors like... another node hearting on the same slot,
 * flaky device that is losing writes, etc.
 * Returns 1 if check succeeds, 0 otherwise.
 */
static int o2hb_check_own_slot(struct o2hb_region *reg)
{}

static inline void o2hb_prepare_block(struct o2hb_region *reg,
				      u64 generation)
{}

static void o2hb_fire_callbacks(struct o2hb_callback *hbcall,
				struct o2nm_node *node,
				int idx)
{}

/* Will run the list in order until we process the passed event */
static void o2hb_run_event_list(struct o2hb_node_event *queued_event)
{}

static void o2hb_queue_node_event(struct o2hb_node_event *event,
				  enum o2hb_callback_type type,
				  struct o2nm_node *node,
				  int node_num)
{}

static void o2hb_shutdown_slot(struct o2hb_disk_slot *slot)
{}

static void o2hb_set_quorum_device(struct o2hb_region *reg)
{}

static int o2hb_check_slot(struct o2hb_region *reg,
			   struct o2hb_disk_slot *slot)
{}

static int o2hb_highest_node(unsigned long *nodes, int numbits)
{}

static int o2hb_lowest_node(unsigned long *nodes, int numbits)
{}

static int o2hb_do_disk_heartbeat(struct o2hb_region *reg)
{}

/*
 * we ride the region ref that the region dir holds.  before the region
 * dir is removed and drops it ref it will wait to tear down this
 * thread.
 */
static int o2hb_thread(void *data)
{}

#ifdef CONFIG_DEBUG_FS
static int o2hb_debug_open(struct inode *inode, struct file *file)
{}

static int o2hb_debug_release(struct inode *inode, struct file *file)
{}

static ssize_t o2hb_debug_read(struct file *file, char __user *buf,
				 size_t nbytes, loff_t *ppos)
{}
#else
static int o2hb_debug_open(struct inode *inode, struct file *file)
{
	return 0;
}
static int o2hb_debug_release(struct inode *inode, struct file *file)
{
	return 0;
}
static ssize_t o2hb_debug_read(struct file *file, char __user *buf,
			       size_t nbytes, loff_t *ppos)
{
	return 0;
}
#endif  /* CONFIG_DEBUG_FS */

static const struct file_operations o2hb_debug_fops =;

void o2hb_exit(void)
{}

static void o2hb_debug_create(const char *name, struct dentry *dir,
			      struct o2hb_debug_buf **db, int db_len, int type,
			      int size, int len, void *data)
{}

static void o2hb_debug_init(void)
{}

void o2hb_init(void)
{}

/* if we're already in a callback then we're already serialized by the sem */
static void o2hb_fill_node_map_from_callback(unsigned long *map,
					     unsigned int bits)
{}

/*
 * get a map of all nodes that are heartbeating in any regions
 */
void o2hb_fill_node_map(unsigned long *map, unsigned int bits)
{}
EXPORT_SYMBOL_GPL();

/*
 * heartbeat configfs bits.  The heartbeat set is a default set under
 * the cluster set in nodemanager.c.
 */

static struct o2hb_region *to_o2hb_region(struct config_item *item)
{}

/* drop_item only drops its ref after killing the thread, nothing should
 * be using the region anymore.  this has to clean up any state that
 * attributes might have built up. */
static void o2hb_region_release(struct config_item *item)
{}

static int o2hb_read_block_input(struct o2hb_region *reg,
				 const char *page,
				 unsigned long *ret_bytes,
				 unsigned int *ret_bits)
{}

static ssize_t o2hb_region_block_bytes_show(struct config_item *item,
					    char *page)
{}

static ssize_t o2hb_region_block_bytes_store(struct config_item *item,
					     const char *page,
					     size_t count)
{}

static ssize_t o2hb_region_start_block_show(struct config_item *item,
					    char *page)
{}

static ssize_t o2hb_region_start_block_store(struct config_item *item,
					     const char *page,
					     size_t count)
{}

static ssize_t o2hb_region_blocks_show(struct config_item *item, char *page)
{}

static ssize_t o2hb_region_blocks_store(struct config_item *item,
					const char *page,
					size_t count)
{}

static ssize_t o2hb_region_dev_show(struct config_item *item, char *page)
{}

static void o2hb_init_region_params(struct o2hb_region *reg)
{}

static int o2hb_map_slot_data(struct o2hb_region *reg)
{}

/* Read in all the slots available and populate the tracking
 * structures so that we can start with a baseline idea of what's
 * there. */
static int o2hb_populate_slot_data(struct o2hb_region *reg)
{}

/*
 * this is acting as commit; we set up all of hr_bdev_file and hr_task or
 * nothing
 */
static ssize_t o2hb_region_dev_store(struct config_item *item,
				     const char *page,
				     size_t count)
{}

static ssize_t o2hb_region_pid_show(struct config_item *item, char *page)
{}

CONFIGFS_ATTR();
CONFIGFS_ATTR();
CONFIGFS_ATTR();
CONFIGFS_ATTR();
CONFIGFS_ATTR_RO();

static struct configfs_attribute *o2hb_region_attrs[] =;

static struct configfs_item_operations o2hb_region_item_ops =;

static const struct config_item_type o2hb_region_type =;

/* heartbeat set */

struct o2hb_heartbeat_group {};

static struct o2hb_heartbeat_group *to_o2hb_heartbeat_group(struct config_group *group)
{}

static void o2hb_debug_region_init(struct o2hb_region *reg,
				   struct dentry *parent)
{}

static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *group,
							  const char *name)
{}

static void o2hb_heartbeat_group_drop_item(struct config_group *group,
					   struct config_item *item)
{}

static ssize_t o2hb_heartbeat_group_dead_threshold_show(struct config_item *item,
		char *page)
{}

static ssize_t o2hb_heartbeat_group_dead_threshold_store(struct config_item *item,
		const char *page, size_t count)
{}

static ssize_t o2hb_heartbeat_group_mode_show(struct config_item *item,
		char *page)
{}

static ssize_t o2hb_heartbeat_group_mode_store(struct config_item *item,
		const char *page, size_t count)
{}

CONFIGFS_ATTR();
CONFIGFS_ATTR();

static struct configfs_attribute *o2hb_heartbeat_group_attrs[] =;

static struct configfs_group_operations o2hb_heartbeat_group_group_ops =;

static const struct config_item_type o2hb_heartbeat_group_type =;

/* this is just here to avoid touching group in heartbeat.h which the
 * entire damn world #includes */
struct config_group *o2hb_alloc_hb_set(void)
{}

void o2hb_free_hb_set(struct config_group *group)
{}

/* hb callback registration and issuing */

static struct o2hb_callback *hbcall_from_type(enum o2hb_callback_type type)
{}

void o2hb_setup_callback(struct o2hb_callback_func *hc,
			 enum o2hb_callback_type type,
			 o2hb_cb_func *func,
			 void *data,
			 int priority)
{}
EXPORT_SYMBOL_GPL();

/*
 * In local heartbeat mode, region_uuid passed matches the dlm domain name.
 * In global heartbeat mode, region_uuid passed is NULL.
 *
 * In local, we only pin the matching region. In global we pin all the active
 * regions.
 */
static int o2hb_region_pin(const char *region_uuid)
{}

/*
 * In local heartbeat mode, region_uuid passed matches the dlm domain name.
 * In global heartbeat mode, region_uuid passed is NULL.
 *
 * In local, we only unpin the matching region. In global we unpin all the
 * active regions.
 */
static void o2hb_region_unpin(const char *region_uuid)
{}

static int o2hb_region_inc_user(const char *region_uuid)
{}

static void o2hb_region_dec_user(const char *region_uuid)
{}

int o2hb_register_callback(const char *region_uuid,
			   struct o2hb_callback_func *hc)
{}
EXPORT_SYMBOL_GPL();

void o2hb_unregister_callback(const char *region_uuid,
			      struct o2hb_callback_func *hc)
{}
EXPORT_SYMBOL_GPL();

int o2hb_check_node_heartbeating_no_sem(u8 node_num)
{}
EXPORT_SYMBOL_GPL();

int o2hb_check_node_heartbeating_from_callback(u8 node_num)
{}
EXPORT_SYMBOL_GPL();

/*
 * this is just a hack until we get the plumbing which flips file systems
 * read only and drops the hb ref instead of killing the node dead.
 */
void o2hb_stop_all_regions(void)
{}
EXPORT_SYMBOL_GPL();

int o2hb_get_all_regions(char *region_uuids, u8 max_regions)
{}
EXPORT_SYMBOL_GPL();

int o2hb_global_heartbeat_active(void)
{}
EXPORT_SYMBOL();