Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

#if !defined(__LOCKDOC_H__)
#define __LOCKDOC_H__

#if defined(X86_BOOT_CODE) && defined(CONFIG_LOCKDOC)
#undef CONFIG_LOCKDOC
#endif

#ifdef CONFIG_LOCKDOC
#include <linux/kernel.h>
#include <linux/irqflags.h>
#include <linux/spinlock.h>
#include <linux/seqlock_types.h>
#include <linux/semaphore.h>
#include <linux/lockdoc_event.h>

#define DELIMITER	"#"
#define DELIMITER_CHAR	'#'
#define PING_CHAR	'p'
#define MK_STRING(x)	#x
#define IO_PORT_LOG	(0x00e9)

#define log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,_irqsync)	{			\
	spinlock_t *__spinlock;										\
	raw_spinlock_t *__rawspinlock;									\
	rwlock_t *__rwlock;										\
	struct mutex *__mutex;										\
	seqlock_t *__seqlock;										\
	const seqlock_t *__constseqlock;										\
	seqcount_t *__seqcount;									\
	const seqcount_t *__constseqcount;									\
	struct semaphore *__semaphore;									\
	struct rw_semaphore *__rwsemaphore;								\
	unsigned long *__bitspinlock;									\
	int status = log_preempt_count();								\
	if (_ptr == (void*)PSEUDOLOCK_ADDR_RCU) {									\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,PSEUDOLOCK_NAME_RCU,status,_irqsync,0);	\
	} else if (_ptr == (void*)PSEUDOLOCK_ADDR_HARDIRQ) {									\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,PSEUDOLOCK_NAME_HARDIRQ,status,_irqsync,0);	\
	} else if (_ptr == (void*)PSEUDOLOCK_ADDR_SOFTIRQ) {									\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,PSEUDOLOCK_NAME_SOFTIRQ,status,_irqsync,0);	\
	} else if (__same_type(_ptr,__spinlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,"spinlock_t",status,_irqsync,0);	\
	} else if (__same_type(_ptr,__rwlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,"rwlock_t",status,_irqsync,0);	\
	} else if (__same_type(_ptr,__mutex)) {								\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,"mutex",status,_irqsync,0);	\
	} else if (__same_type(_ptr,__seqlock) || __same_type(_ptr,__constseqlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "seqlock_t",status,_irqsync,0);	\
	} else if (__same_type(_ptr,__seqcount) || __same_type(_ptr,__constseqcount)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "seqcount_t",status,_irqsync,0);	\
	} else if (__same_type(_ptr,__rawspinlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "raw_spinlock_t",status,_irqsync,0);	\
	} else if (__same_type(_ptr,__semaphore)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "semaphore",status,_irqsync,0);	\
	} else if (__same_type(_ptr,__rwsemaphore)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "rw_semaphore",status,_irqsync,0);	\
	} else if (__same_type(_ptr,__bitspinlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "bit_spin_lock",status,_irqsync,0);	\
	} else{												\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "unknown",status,_irqsync,0);	\
	}												\
}

extern struct log_action la_buffer;
int log_preempt_count(void);

/* Basic port I/O */
static inline void outb_(u8 v, u16 port)
{
	asm volatile("outb %0,%1" : : "a" (v), "dN" (port));
}

int32_t lockdoc_get_ctx(void);

static inline void log_memory(int alloc, const char *datatype, const void *ptr, size_t size) {
	unsigned long flags;
	
	raw_local_irq_save(flags);

	memset(&la_buffer,0,sizeof(la_buffer));

	la_buffer.action = (alloc == 1 ? LOCKDOC_ALLOC : LOCKDOC_FREE);
	la_buffer.ctx = lockdoc_get_ctx();
	la_buffer.ptr = (unsigned long)ptr;
	la_buffer.size = size;
	/*
	 * One could use a more safe string function, e.g., strlcpy. 
	 * However, we want these *log* functions to be fast.
	 * We therefore skip all sanity checks, and all that stuff.
	 * To ensure any string buffer contains a valid string, we
	 * always write a NULL byte at its end.
	 */
	strncpy(la_buffer.type,datatype,LOG_CHAR_BUFFER_LEN);
	la_buffer.type[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	outb_(PING_CHAR,IO_PORT_LOG);

	raw_local_irq_restore(flags);
}

/*
 * The parameter irqsync should actually be an enum IRQ_SYNC. To avoid circular dependencies in include/linux/{irqflags,bottom_half}.h which contains
 * a declaration of this function, this parameter has to be an int.
 */
static inline void __log_lock(int lock_op, const char *lock_member, const void *ptr, const char *file, int line, const char *function, const char *lock_type, int preempt_count, int irq_sync, int flags) {
	unsigned long eflags;

	raw_local_irq_save(eflags);

	memset(&la_buffer,0,sizeof(la_buffer));

	la_buffer.action = LOCKDOC_LOCK_OP;
	la_buffer.ctx = lockdoc_get_ctx();
	la_buffer.lock_op = lock_op;
	la_buffer.ptr = (unsigned long)ptr;
	strncpy(la_buffer.type,lock_type,LOG_CHAR_BUFFER_LEN);
	la_buffer.type[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	strncpy(la_buffer.lock_member,lock_member,LOG_CHAR_BUFFER_LEN);
	la_buffer.lock_member[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	strncpy(la_buffer.file,file,LOG_CHAR_BUFFER_LEN);
	la_buffer.file[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	strncpy(la_buffer.function,function,LOG_CHAR_BUFFER_LEN);
	la_buffer.line = line;
	la_buffer.function[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	la_buffer.preempt_count = preempt_count;
	la_buffer.irq_sync = irq_sync;
	la_buffer.flags = flags;
	outb_(PING_CHAR,IO_PORT_LOG);

	raw_local_irq_restore(eflags);
}

void lockdoc_send_current_task_addr(void);
void lockdoc_send_preempt_count_addr(void);
void lockdoc_send_pid_offset(void);
void lockdoc_send_kernel_version(void);
#else
#define MK_STRING(x)	#x
#define log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,_logFn)
#define log_memory(alloc, datatype, ptr, size)
#define lockdoc_send_current_task_addr()
#define lockdoc_send_preempt_count_addr()
#define lockdoc_send_pid_offset()
#define lockdoc_send_kernel_version()
#endif /* !CONFIG_LOCKDOC */

#endif // __LOCKDOC_H__