928 lines · 19.8 KB · C
RawDownload
#include <linux/module.h> #include <linux/moduleparam.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/blkdev.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/blk-mq.h>#include <linux/hrtimer.h>#include <linux/lightnvm.h> struct nullb_cmd {	struct list_head list;	struct llist_node ll_list;	struct call_single_data csd;	struct request *rq;	struct bio *bio;	unsigned int tag;	struct nullb_queue *nq;	struct hrtimer timer;}; struct nullb_queue {	unsigned long *tag_map;	wait_queue_head_t wait;	unsigned int queue_depth; 	struct nullb_cmd *cmds;}; struct nullb {	struct list_head list;	unsigned int index;	struct request_queue *q;	struct gendisk *disk;	struct nvm_dev *ndev;	struct blk_mq_tag_set *tag_set;	struct blk_mq_tag_set __tag_set;	struct hrtimer timer;	unsigned int queue_depth;	spinlock_t lock; 	struct nullb_queue *queues;	unsigned int nr_queues;	char disk_name[DISK_NAME_LEN];}; static LIST_HEAD(nullb_list);static struct mutex lock;static int null_major;static int nullb_indexes;static struct kmem_cache *ppa_cache;static struct blk_mq_tag_set tag_set; enum {	NULL_IRQ_NONE		= 0,	NULL_IRQ_SOFTIRQ	= 1,	NULL_IRQ_TIMER		= 2,}; enum {	NULL_Q_BIO		= 0,	NULL_Q_RQ		= 1,	NULL_Q_MQ		= 2,}; static int submit_queues;module_param(submit_queues, int, S_IRUGO);MODULE_PARM_DESC(submit_queues, "Number of submission queues"); static int home_node = NUMA_NO_NODE;module_param(home_node, int, S_IRUGO);MODULE_PARM_DESC(home_node, "Home node for the device"); static int queue_mode = NULL_Q_MQ; static int null_param_store_val(const char *str, int *val, int min, int max){	int ret, new_val; 	ret = kstrtoint(str, 10, &new_val);	if (ret)		return -EINVAL; 	if (new_val < min || new_val > max)		return -EINVAL; 	*val = new_val;	return 0;} static int null_set_queue_mode(const char *str, const struct kernel_param *kp){	return null_param_store_val(str, &queue_mode, NULL_Q_BIO, NULL_Q_MQ);} static const struct kernel_param_ops null_queue_mode_param_ops = {	.set	= null_set_queue_mode,	.get	= param_get_int,}; device_param_cb(queue_mode, &null_queue_mode_param_ops, &queue_mode, S_IRUGO);MODULE_PARM_DESC(queue_mode, "Block interface to use (0=bio,1=rq,2=multiqueue)"); static int gb = 250;module_param(gb, int, S_IRUGO);MODULE_PARM_DESC(gb, "Size in GB"); static int bs = 512;module_param(bs, int, S_IRUGO);MODULE_PARM_DESC(bs, "Block size (in bytes)"); static int nr_devices = 1;module_param(nr_devices, int, S_IRUGO);MODULE_PARM_DESC(nr_devices, "Number of devices to register"); static bool use_lightnvm;module_param(use_lightnvm, bool, S_IRUGO);MODULE_PARM_DESC(use_lightnvm, "Register as a LightNVM device"); static bool blocking;module_param(blocking, bool, S_IRUGO);MODULE_PARM_DESC(blocking, "Register as a blocking blk-mq driver device"); static bool shared_tags;module_param(shared_tags, bool, S_IRUGO);MODULE_PARM_DESC(shared_tags, "Share tag set between devices for blk-mq"); static int irqmode = NULL_IRQ_SOFTIRQ; static int null_set_irqmode(const char *str, const struct kernel_param *kp){	return null_param_store_val(str, &irqmode, NULL_IRQ_NONE,					NULL_IRQ_TIMER);} static const struct kernel_param_ops null_irqmode_param_ops = {	.set	= null_set_irqmode,	.get	= param_get_int,}; device_param_cb(irqmode, &null_irqmode_param_ops, &irqmode, S_IRUGO);MODULE_PARM_DESC(irqmode, "IRQ completion handler. 0-none, 1-softirq, 2-timer"); static unsigned long completion_nsec = 10000;module_param(completion_nsec, ulong, S_IRUGO);MODULE_PARM_DESC(completion_nsec, "Time in ns to complete a request in hardware. Default: 10,000ns"); static int hw_queue_depth = 64;module_param(hw_queue_depth, int, S_IRUGO);MODULE_PARM_DESC(hw_queue_depth, "Queue depth for each hardware queue. Default: 64"); static bool use_per_node_hctx = false;module_param(use_per_node_hctx, bool, S_IRUGO);MODULE_PARM_DESC(use_per_node_hctx, "Use per-node allocation for hardware context queues. Default: false"); static void put_tag(struct nullb_queue *nq, unsigned int tag){	clear_bit_unlock(tag, nq->tag_map); 	if (waitqueue_active(&nq->wait))		wake_up(&nq->wait);} static unsigned int get_tag(struct nullb_queue *nq){	unsigned int tag; 	do {		tag = find_first_zero_bit(nq->tag_map, nq->queue_depth);		if (tag >= nq->queue_depth)			return -1U;	} while (test_and_set_bit_lock(tag, nq->tag_map)); 	return tag;} static void free_cmd(struct nullb_cmd *cmd){	put_tag(cmd->nq, cmd->tag);} static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer); static struct nullb_cmd *__alloc_cmd(struct nullb_queue *nq){	struct nullb_cmd *cmd;	unsigned int tag; 	tag = get_tag(nq);	if (tag != -1U) {		cmd = &nq->cmds[tag];		cmd->tag = tag;		cmd->nq = nq;		if (irqmode == NULL_IRQ_TIMER) {			hrtimer_init(&cmd->timer, CLOCK_MONOTONIC,				     HRTIMER_MODE_REL);			cmd->timer.function = null_cmd_timer_expired;		}		return cmd;	} 	return NULL;} static struct nullb_cmd *alloc_cmd(struct nullb_queue *nq, int can_wait){	struct nullb_cmd *cmd;	DEFINE_WAIT(wait); 	cmd = __alloc_cmd(nq);	if (cmd || !can_wait)		return cmd; 	do {		prepare_to_wait(&nq->wait, &wait, TASK_UNINTERRUPTIBLE);		cmd = __alloc_cmd(nq);		if (cmd)			break; 		io_schedule();	} while (1); 	finish_wait(&nq->wait, &wait);	return cmd;} static void end_cmd(struct nullb_cmd *cmd){	struct request_queue *q = NULL; 	if (cmd->rq)		q = cmd->rq->q; 	switch (queue_mode)  {	case NULL_Q_MQ:		blk_mq_end_request(cmd->rq, BLK_STS_OK);		return;	case NULL_Q_RQ:		INIT_LIST_HEAD(&cmd->rq->queuelist);		blk_end_request_all(cmd->rq, BLK_STS_OK);		break;	case NULL_Q_BIO:		bio_endio(cmd->bio);		break;	} 	free_cmd(cmd); 	/* Restart queue if needed, as we are freeing a tag */	if (queue_mode == NULL_Q_RQ && blk_queue_stopped(q)) {		unsigned long flags; 		spin_lock_irqsave(q->queue_lock, flags);		blk_start_queue_async(q);		spin_unlock_irqrestore(q->queue_lock, flags);	}} static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer){	end_cmd(container_of(timer, struct nullb_cmd, timer)); 	return HRTIMER_NORESTART;} static void null_cmd_end_timer(struct nullb_cmd *cmd){	ktime_t kt = completion_nsec; 	hrtimer_start(&cmd->timer, kt, HRTIMER_MODE_REL);} static void null_softirq_done_fn(struct request *rq){	if (queue_mode == NULL_Q_MQ)		end_cmd(blk_mq_rq_to_pdu(rq));	else		end_cmd(rq->special);} static inline void null_handle_cmd(struct nullb_cmd *cmd){	/* Complete IO by inline, softirq or timer */	switch (irqmode) {	case NULL_IRQ_SOFTIRQ:		switch (queue_mode)  {		case NULL_Q_MQ:			blk_mq_complete_request(cmd->rq);			break;		case NULL_Q_RQ:			blk_complete_request(cmd->rq);			break;		case NULL_Q_BIO:			/*			 * XXX: no proper submitting cpu information available.			 */			end_cmd(cmd);			break;		}		break;	case NULL_IRQ_NONE:		end_cmd(cmd);		break;	case NULL_IRQ_TIMER:		null_cmd_end_timer(cmd);		break;	}} static struct nullb_queue *nullb_to_queue(struct nullb *nullb){	int index = 0; 	if (nullb->nr_queues != 1)		index = raw_smp_processor_id() / ((nr_cpu_ids + nullb->nr_queues - 1) / nullb->nr_queues); 	return &nullb->queues[index];} static blk_qc_t null_queue_bio(struct request_queue *q, struct bio *bio){	struct nullb *nullb = q->queuedata;	struct nullb_queue *nq = nullb_to_queue(nullb);	struct nullb_cmd *cmd; 	cmd = alloc_cmd(nq, 1);	cmd->bio = bio; 	null_handle_cmd(cmd);	return BLK_QC_T_NONE;} static int null_rq_prep_fn(struct request_queue *q, struct request *req){	struct nullb *nullb = q->queuedata;	struct nullb_queue *nq = nullb_to_queue(nullb);	struct nullb_cmd *cmd; 	cmd = alloc_cmd(nq, 0);	if (cmd) {		cmd->rq = req;		req->special = cmd;		return BLKPREP_OK;	}	blk_stop_queue(q); 	return BLKPREP_DEFER;} static void null_request_fn(struct request_queue *q){	struct request *rq; 	while ((rq = blk_fetch_request(q)) != NULL) {		struct nullb_cmd *cmd = rq->special; 		spin_unlock_irq(q->queue_lock);		null_handle_cmd(cmd);		spin_lock_irq(q->queue_lock);	}} static blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx,			 const struct blk_mq_queue_data *bd){	struct nullb_cmd *cmd = blk_mq_rq_to_pdu(bd->rq); 	might_sleep_if(hctx->flags & BLK_MQ_F_BLOCKING); 	if (irqmode == NULL_IRQ_TIMER) {		hrtimer_init(&cmd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);		cmd->timer.function = null_cmd_timer_expired;	}	cmd->rq = bd->rq;	cmd->nq = hctx->driver_data; 	blk_mq_start_request(bd->rq); 	null_handle_cmd(cmd);	return BLK_STS_OK;} static const struct blk_mq_ops null_mq_ops = {	.queue_rq       = null_queue_rq,	.complete	= null_softirq_done_fn,}; static void cleanup_queue(struct nullb_queue *nq){	kfree(nq->tag_map);	kfree(nq->cmds);} static void cleanup_queues(struct nullb *nullb){	int i; 	for (i = 0; i < nullb->nr_queues; i++)		cleanup_queue(&nullb->queues[i]); 	kfree(nullb->queues);} #ifdef CONFIG_NVM static void null_lnvm_end_io(struct request *rq, blk_status_t status){	struct nvm_rq *rqd = rq->end_io_data; 	/* XXX: lighnvm core seems to expect NVM_RSP_* values here.. */	rqd->error = status ? -EIO : 0;	nvm_end_io(rqd); 	blk_put_request(rq);} static int null_lnvm_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd){	struct request_queue *q = dev->q;	struct request *rq;	struct bio *bio = rqd->bio; 	rq = blk_mq_alloc_request(q,		op_is_write(bio_op(bio)) ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0);	if (IS_ERR(rq))		return -ENOMEM; 	blk_init_request_from_bio(rq, bio); 	rq->end_io_data = rqd; 	blk_execute_rq_nowait(q, NULL, rq, 0, null_lnvm_end_io); 	return 0;} static int null_lnvm_id(struct nvm_dev *dev, struct nvm_id *id){	sector_t size = gb * 1024 * 1024 * 1024ULL;	sector_t blksize;	struct nvm_id_group *grp; 	id->ver_id = 0x1;	id->vmnt = 0;	id->cap = 0x2;	id->dom = 0x1; 	id->ppaf.blk_offset = 0;	id->ppaf.blk_len = 16;	id->ppaf.pg_offset = 16;	id->ppaf.pg_len = 16;	id->ppaf.sect_offset = 32;	id->ppaf.sect_len = 8;	id->ppaf.pln_offset = 40;	id->ppaf.pln_len = 8;	id->ppaf.lun_offset = 48;	id->ppaf.lun_len = 8;	id->ppaf.ch_offset = 56;	id->ppaf.ch_len = 8; 	sector_div(size, bs); /* convert size to pages */	size >>= 8; /* concert size to pgs pr blk */	grp = &id->grp;	grp->mtype = 0;	grp->fmtype = 0;	grp->num_ch = 1;	grp->num_pg = 256;	blksize = size;	size >>= 16;	grp->num_lun = size + 1;	sector_div(blksize, grp->num_lun);	grp->num_blk = blksize;	grp->num_pln = 1; 	grp->fpg_sz = bs;	grp->csecs = bs;	grp->trdt = 25000;	grp->trdm = 25000;	grp->tprt = 500000;	grp->tprm = 500000;	grp->tbet = 1500000;	grp->tbem = 1500000;	grp->mpos = 0x010101; /* single plane rwe */	grp->cpar = hw_queue_depth; 	return 0;} static void *null_lnvm_create_dma_pool(struct nvm_dev *dev, char *name){	mempool_t *virtmem_pool; 	virtmem_pool = mempool_create_slab_pool(64, ppa_cache);	if (!virtmem_pool) {		pr_err("null_blk: Unable to create virtual memory pool\n");		return NULL;	} 

Lines 1–500 of 928.