cregit-Linux how code gets into the kernel

Release 4.14 drivers/rapidio/rio_cm.c

Directory: drivers/rapidio
/*
 * rio_cm - RapidIO Channelized Messaging Driver
 *
 * Copyright 2013-2016 Integrated Device Technology, Inc.
 * Copyright (c) 2015, Prodrive Technologies
 * Copyright (c) 2015, RapidIO Trade Association
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 *
 * THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
 * BUT WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  SEE THE
 * GNU GENERAL PUBLIC LICENSE FOR MORE DETAILS.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/rio.h>
#include <linux/rio_drv.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/reboot.h>
#include <linux/bitops.h>
#include <linux/printk.h>
#include <linux/rio_cm_cdev.h>


#define DRV_NAME        "rio_cm"

#define DRV_VERSION     "1.0.0"

#define DRV_AUTHOR      "Alexandre Bounine <alexandre.bounine@idt.com>"

#define DRV_DESC        "RapidIO Channelized Messaging Driver"

#define DEV_NAME	"rio_cm"

/* Debug output filtering masks */
enum {
	
DBG_NONE	= 0,
	
DBG_INIT	= BIT(0), /* driver init */
	
DBG_EXIT	= BIT(1), /* driver exit */
	
DBG_MPORT	= BIT(2), /* mport add/remove */
	
DBG_RDEV	= BIT(3), /* RapidIO device add/remove */
	
DBG_CHOP	= BIT(4), /* channel operations */
	
DBG_WAIT	= BIT(5), /* waiting for events */
	
DBG_TX		= BIT(6), /* message TX */
	
DBG_TX_EVENT	= BIT(7), /* message TX event */
	
DBG_RX_DATA	= BIT(8), /* inbound data messages */
	
DBG_RX_CMD	= BIT(9), /* inbound REQ/ACK/NACK messages */
	
DBG_ALL		= ~0,
};

#ifdef DEBUG

#define riocm_debug(level, fmt, arg...) \
	do { \
                if (DBG_##level & dbg_level) \
                        pr_debug(DRV_NAME ": %s " fmt "\n", \
                                __func__, ##arg); \
        } while (0)
#else

#define riocm_debug(level, fmt, arg...) \
		no_printk(KERN_DEBUG pr_fmt(DRV_NAME fmt "\n"), ##arg)
#endif


#define riocm_warn(fmt, arg...) \
	pr_warn(DRV_NAME ": %s WARNING " fmt "\n", __func__, ##arg)


#define riocm_error(fmt, arg...) \
	pr_err(DRV_NAME ": %s ERROR " fmt "\n", __func__, ##arg)



static int cmbox = 1;
module_param(cmbox, int, S_IRUGO);
MODULE_PARM_DESC(cmbox, "RapidIO Mailbox number (default 1)");


static int chstart = 256;
module_param(chstart, int, S_IRUGO);
MODULE_PARM_DESC(chstart,
		 "Start channel number for dynamic allocation (default 256)");

#ifdef DEBUG

static u32 dbg_level = DBG_NONE;
module_param(dbg_level, uint, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(dbg_level, "Debugging output level (default 0 = none)");
#endif


MODULE_AUTHOR(DRV_AUTHOR);

MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");

MODULE_VERSION(DRV_VERSION);


#define RIOCM_TX_RING_SIZE	128

#define RIOCM_RX_RING_SIZE	128

#define RIOCM_CONNECT_TO	3 
/* connect response TO (in sec) */


#define RIOCM_MAX_CHNUM		0xffff 
/* Use full range of u16 field */

#define RIOCM_CHNUM_AUTO	0

#define RIOCM_MAX_EP_COUNT	0x10000 
/* Max number of endpoints */


enum rio_cm_state {
	
RIO_CM_IDLE,
	
RIO_CM_CONNECT,
	
RIO_CM_CONNECTED,
	
RIO_CM_DISCONNECT,
	
RIO_CM_CHAN_BOUND,
	
RIO_CM_LISTEN,
	
RIO_CM_DESTROYING,
};


enum rio_cm_pkt_type {
	
RIO_CM_SYS	= 0xaa,
	
RIO_CM_CHAN	= 0x55,
};


enum rio_cm_chop {
	
CM_CONN_REQ,
	
CM_CONN_ACK,
	
CM_CONN_CLOSE,
	
CM_DATA_MSG,
};


struct rio_ch_base_bhdr {
	
u32 src_id;
	
u32 dst_id;

#define RIO_HDR_LETTER_MASK 0xffff0000

#define RIO_HDR_MBOX_MASK   0x0000ffff
	
u8  src_mbox;
	
u8  dst_mbox;
	
u8  type;
} __attribute__((__packed__));


struct rio_ch_chan_hdr {
	
struct rio_ch_base_bhdr bhdr;
	
u8 ch_op;
	
u16 dst_ch;
	
u16 src_ch;
	
u16 msg_len;
	
u16 rsrvd;
} __attribute__((__packed__));


struct tx_req {
	
struct list_head node;
	
struct rio_dev   *rdev;
	
void		 *buffer;
	
size_t		 len;
};


struct cm_dev {
	
struct list_head	list;
	
struct rio_mport	*mport;
	
void			*rx_buf[RIOCM_RX_RING_SIZE];
	
int			rx_slots;
	
struct mutex		rx_lock;

	
void			*tx_buf[RIOCM_TX_RING_SIZE];
	
int			tx_slot;
	
int			tx_cnt;
	
int			tx_ack_slot;
	
struct list_head	tx_reqs;
	
spinlock_t		tx_lock;

	
struct list_head	peers;
	
u32			npeers;
	
struct workqueue_struct *rx_wq;
	
struct work_struct	rx_work;
};


struct chan_rx_ring {
	
void	*buf[RIOCM_RX_RING_SIZE];
	
int	head;
	
int	tail;
	
int	count;

	/* Tracking RX buffers reported to upper level */
	
void	*inuse[RIOCM_RX_RING_SIZE];
	
int	inuse_cnt;
};


struct rio_channel {
	
u16			id;	/* local channel ID */
	
struct kref		ref;	/* channel refcount */
	
struct file		*filp;
	
struct cm_dev		*cmdev;	/* associated CM device object */
	
struct rio_dev		*rdev;	/* remote RapidIO device */
	
enum rio_cm_state	state;
	
int			error;
	
spinlock_t		lock;
	
void			*context;
	
u32			loc_destid;	/* local destID */
	
u32			rem_destid;	/* remote destID */
	
u16			rem_channel;	/* remote channel ID */
	
struct list_head	accept_queue;
	
struct list_head	ch_node;
	
struct completion	comp;
	
struct completion	comp_close;
	
struct chan_rx_ring	rx_ring;
};


struct cm_peer {
	
struct list_head node;
	
struct rio_dev *rdev;
};


struct rio_cm_work {
	
struct work_struct work;
	
struct cm_dev *cm;
	
void *data;
};


struct conn_req {
	
struct list_head node;
	
u32 destid;	/* requester destID */
	
u16 chan;	/* requester channel ID */
	
struct cm_dev *cmdev;
};

/*
 * A channel_dev structure represents a CM_CDEV
 * @cdev        Character device
 * @dev         Associated device object
 */

struct channel_dev {
	
struct cdev	cdev;
	
struct device	*dev;
};

static struct rio_channel *riocm_ch_alloc(u16 ch_num);
static void riocm_ch_free(struct kref *ref);
static int riocm_post_send(struct cm_dev *cm, struct rio_dev *rdev,
			   void *buffer, size_t len);
static int riocm_ch_close(struct rio_channel *ch);

static DEFINE_SPINLOCK(idr_lock);
static DEFINE_IDR(ch_idr);

static LIST_HEAD(cm_dev_list);
static DECLARE_RWSEM(rdev_sem);


static struct class *dev_class;

static unsigned int dev_major;

static unsigned int dev_minor_base;

static dev_t dev_number;

static struct channel_dev riocm_cdev;


#define is_msg_capable(src_ops, dst_ops)			\
			((src_ops & RIO_SRC_OPS_DATA_MSG) &&    \
                         (dst_ops & RIO_DST_OPS_DATA_MSG))

#define dev_cm_capable(dev) \
	is_msg_capable(dev->src_ops, dev->dst_ops)


static int riocm_cmp(struct rio_channel *ch, enum rio_cm_state cmp) { int ret; spin_lock_bh(&ch->lock); ret = (ch->state == cmp); spin_unlock_bh(&ch->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine47100.00%1100.00%
Total47100.00%1100.00%


static int riocm_cmp_exch(struct rio_channel *ch, enum rio_cm_state cmp, enum rio_cm_state exch) { int ret; spin_lock_bh(&ch->lock); ret = (ch->state == cmp); if (ret) ch->state = exch; spin_unlock_bh(&ch->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine61100.00%1100.00%
Total61100.00%1100.00%


static enum rio_cm_state riocm_exch(struct rio_channel *ch, enum rio_cm_state exch) { enum rio_cm_state old; spin_lock_bh(&ch->lock); old = ch->state; ch->state = exch; spin_unlock_bh(&ch->lock); return old; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine51100.00%1100.00%
Total51100.00%1100.00%


static struct rio_channel *riocm_get_channel(u16 nr) { struct rio_channel *ch; spin_lock_bh(&idr_lock); ch = idr_find(&ch_idr, nr); if (ch) kref_get(&ch->ref); spin_unlock_bh(&idr_lock); return ch; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine53100.00%1100.00%
Total53100.00%1100.00%


static void riocm_put_channel(struct rio_channel *ch) { kref_put(&ch->ref, riocm_ch_free); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine21100.00%1100.00%
Total21100.00%1100.00%


static void *riocm_rx_get_msg(struct cm_dev *cm) { void *msg; int i; msg = rio_get_inb_message(cm->mport, cmbox); if (msg) { for (i = 0; i < RIOCM_RX_RING_SIZE; i++) { if (cm->rx_buf[i] == msg) { cm->rx_buf[i] = NULL; cm->rx_slots++; break; } } if (i == RIOCM_RX_RING_SIZE) riocm_warn("no record for buffer 0x%p", msg); } return msg; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine95100.00%1100.00%
Total95100.00%1100.00%

/* * riocm_rx_fill - fills a ring of receive buffers for given cm device * @cm: cm_dev object * @nent: max number of entries to fill * * Returns: none */
static void riocm_rx_fill(struct cm_dev *cm, int nent) { int i; if (cm->rx_slots == 0) return; for (i = 0; i < RIOCM_RX_RING_SIZE && cm->rx_slots && nent; i++) { if (cm->rx_buf[i] == NULL) { cm->rx_buf[i] = kmalloc(RIO_MAX_MSG_SIZE, GFP_KERNEL); if (cm->rx_buf[i] == NULL) break; rio_add_inb_buffer(cm->mport, cmbox, cm->rx_buf[i]); cm->rx_slots--; nent--; } } }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine110100.00%1100.00%
Total110100.00%1100.00%

/* * riocm_rx_free - frees all receive buffers associated with given cm device * @cm: cm_dev object * * Returns: none */
static void riocm_rx_free(struct cm_dev *cm) { int i; for (i = 0; i < RIOCM_RX_RING_SIZE; i++) { if (cm->rx_buf[i] != NULL) { kfree(cm->rx_buf[i]); cm->rx_buf[i] = NULL; } } }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine61100.00%1100.00%
Total61100.00%1100.00%

/* * riocm_req_handler - connection request handler * @cm: cm_dev object * @req_data: pointer to the request packet * * Returns: 0 if success, or * -EINVAL if channel is not in correct state, * -ENODEV if cannot find a channel with specified ID, * -ENOMEM if unable to allocate memory to store the request */
static int riocm_req_handler(struct cm_dev *cm, void *req_data) { struct rio_channel *ch; struct conn_req *req; struct rio_ch_chan_hdr *hh = req_data; u16 chnum; chnum = ntohs(hh->dst_ch); ch = riocm_get_channel(chnum); if (!ch) return -ENODEV; if (ch->state != RIO_CM_LISTEN) { riocm_debug(RX_CMD, "channel %d is not in listen state", chnum); riocm_put_channel(ch); return -EINVAL; } req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) { riocm_put_channel(ch); return -ENOMEM; } req->destid = ntohl(hh->bhdr.src_id); req->chan = ntohs(hh->src_ch); req->cmdev = cm; spin_lock_bh(&ch->lock); list_add_tail(&req->node, &ch->accept_queue); spin_unlock_bh(&ch->lock); complete(&ch->comp); riocm_put_channel(ch); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine192100.00%1100.00%
Total192100.00%1100.00%

/* * riocm_resp_handler - response to connection request handler * @resp_data: pointer to the response packet * * Returns: 0 if success, or * -EINVAL if channel is not in correct state, * -ENODEV if cannot find a channel with specified ID, */
static int riocm_resp_handler(void *resp_data) { struct rio_channel *ch; struct rio_ch_chan_hdr *hh = resp_data; u16 chnum; chnum = ntohs(hh->dst_ch); ch = riocm_get_channel(chnum); if (!ch) return -ENODEV; if (ch->state != RIO_CM_CONNECT) { riocm_put_channel(ch); return -EINVAL; } riocm_exch(ch, RIO_CM_CONNECTED); ch->rem_channel = ntohs(hh->src_ch); complete(&ch->comp); riocm_put_channel(ch); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine103100.00%1100.00%
Total103100.00%1100.00%

/* * riocm_close_handler - channel close request handler * @req_data: pointer to the request packet * * Returns: 0 if success, or * -ENODEV if cannot find a channel with specified ID, * + error codes returned by riocm_ch_close. */
static int riocm_close_handler(void *data) { struct rio_channel *ch; struct rio_ch_chan_hdr *hh = data; int ret; riocm_debug(RX_CMD, "for ch=%d", ntohs(hh->dst_ch)); spin_lock_bh(&idr_lock); ch = idr_find(&ch_idr, ntohs(hh->dst_ch)); if (!ch) { spin_unlock_bh(&idr_lock); return -ENODEV; } idr_remove(&ch_idr, ch->id); spin_unlock_bh(&idr_lock); riocm_exch(ch, RIO_CM_DISCONNECT); ret = riocm_ch_close(ch); if (ret) riocm_debug(RX_CMD, "riocm_ch_close() returned %d", ret); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine123100.00%1100.00%
Total123100.00%1100.00%

/* * rio_cm_handler - function that services request (non-data) packets * @cm: cm_dev object * @data: pointer to the packet */
static void rio_cm_handler(struct cm_dev *cm, void *data) { struct rio_ch_chan_hdr *hdr; if (!rio_mport_is_running(cm->mport)) goto out; hdr = data; riocm_debug(RX_CMD, "OP=%x for ch=%d from %d", hdr->ch_op, ntohs(hdr->dst_ch), ntohs(hdr->src_ch)); switch (hdr->ch_op) { case CM_CONN_REQ: riocm_req_handler(cm, data); break; case CM_CONN_ACK: riocm_resp_handler(data); break; case CM_CONN_CLOSE: riocm_close_handler(data); break; default: riocm_error("Invalid packet header"); break; } out: kfree(data); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine113100.00%1100.00%
Total113100.00%1100.00%

/* * rio_rx_data_handler - received data packet handler * @cm: cm_dev object * @buf: data packet * * Returns: 0 if success, or * -ENODEV if cannot find a channel with specified ID, * -EIO if channel is not in CONNECTED state, * -ENOMEM if channel RX queue is full (packet discarded) */
static int rio_rx_data_handler(struct cm_dev *cm, void *buf) { struct rio_ch_chan_hdr *hdr; struct rio_channel *ch; hdr = buf; riocm_debug(RX_DATA, "for ch=%d", ntohs(hdr->dst_ch)); ch = riocm_get_channel(ntohs(hdr->dst_ch)); if (!ch) { /* Discard data message for non-existing channel */ kfree(buf); return -ENODEV; } /* Place pointer to the buffer into channel's RX queue */ spin_lock(&ch->lock); if (ch->state != RIO_CM_CONNECTED) { /* Channel is not ready to receive data, discard a packet */ riocm_debug(RX_DATA, "ch=%d is in wrong state=%d", ch->id, ch->state); spin_unlock(&ch->lock); kfree(buf); riocm_put_channel(ch); return -EIO; } if (ch->rx_ring.count == RIOCM_RX_RING_SIZE) { /* If RX ring is full, discard a packet */ riocm_debug(RX_DATA, "ch=%d is full", ch->id); spin_unlock(&ch->lock); kfree(buf); riocm_put_channel(ch); return -ENOMEM; } ch->rx_ring.buf[ch->rx_ring.head] = buf; ch->rx_ring.head++; ch->rx_ring.count++; ch->rx_ring.head %= RIOCM_RX_RING_SIZE; complete(&ch->comp); spin_unlock(&ch->lock); riocm_put_channel(ch); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine236100.00%1100.00%
Total236100.00%1100.00%

/* * rio_ibmsg_handler - inbound message packet handler */
static void rio_ibmsg_handler(struct work_struct *work) { struct cm_dev *cm = container_of(work, struct cm_dev, rx_work); void *data; struct rio_ch_chan_hdr *hdr; if (!rio_mport_is_running(cm->mport)) return; while (1) { mutex_lock(&cm->rx_lock); data = riocm_rx_get_msg(cm); if (data) riocm_rx_fill(cm, 1); mutex_unlock(&cm->rx_lock); if (data == NULL) break; hdr = data; if (hdr->bhdr.type != RIO_CM_CHAN) { /* For now simply discard packets other than channel */ riocm_error("Unsupported TYPE code (0x%x). Msg dropped", hdr->bhdr.type); kfree(data); continue; } /* Process a channel message */ if (hdr->ch_op == CM_DATA_MSG) rio_rx_data_handler(cm, data); else rio_cm_handler(cm, data); } }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine151100.00%1100.00%
Total151100.00%1100.00%


static void riocm_inb_msg_event(struct rio_mport *mport, void *dev_id, int mbox, int slot) { struct cm_dev *cm = dev_id; if (rio_mport_is_running(cm->mport) && !work_pending(&cm->rx_work)) queue_work(cm->rx_wq, &cm->rx_work); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine58100.00%1100.00%
Total58100.00%1100.00%

/* * rio_txcq_handler - TX completion handler * @cm: cm_dev object * @slot: TX queue slot * * TX completion handler also ensures that pending request packets are placed * into transmit queue as soon as a free slot becomes available. This is done * to give higher priority to request packets during high intensity data flow. */
static void rio_txcq_handler(struct cm_dev *cm, int slot) { int ack_slot; /* ATTN: Add TX completion notification if/when direct buffer * transfer is implemented. At this moment only correct tracking * of tx_count is important. */ riocm_debug(TX_EVENT, "for mport_%d slot %d tx_cnt %d", cm->mport->id, slot, cm->tx_cnt); spin_lock(&cm->tx_lock); ack_slot = cm->tx_ack_slot; if (ack_slot == slot) riocm_debug(TX_EVENT, "slot == ack_slot"); while (cm->tx_cnt && ((ack_slot != slot) || (cm->tx_cnt == RIOCM_TX_RING_SIZE))) { cm->tx_buf[ack_slot] = NULL; ++ack_slot; ack_slot &= (RIOCM_TX_RING_SIZE - 1); cm->tx_cnt--; } if (cm->tx_cnt < 0 || cm->tx_cnt > RIOCM_TX_RING_SIZE) riocm_error("tx_cnt %d out of sync", cm->tx_cnt); WARN_ON((cm->tx_cnt < 0) || (cm->tx_cnt > RIOCM_TX_RING_SIZE)); cm->tx_ack_slot = ack_slot; /* * If there are pending requests, insert them into transmit queue */ if (!list_empty(&cm->tx_reqs) && (cm->tx_cnt < RIOCM_TX_RING_SIZE)) { struct tx_req *req, *_req; int rc; list_for_each_entry_safe(req, _req, &cm->tx_reqs, node) { list_del(&req->node); cm->tx_buf[cm->tx_slot] = req->buffer; rc = rio_add_outb_message(cm->mport, req->rdev, cmbox, req->buffer, req->len); kfree(req->buffer); kfree(req); ++cm->tx_cnt; ++cm->tx_slot; cm->tx_slot &= (RIOCM_TX_RING_SIZE - 1); if (cm->tx_cnt == RIOCM_TX_RING_SIZE) break; } } spin_unlock(&cm->tx_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine299100.00%1100.00%
Total299100.00%1100.00%


static void riocm_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbox, int slot) { struct cm_dev *cm = dev_id; if (cm && rio_mport_is_running(cm->mport)) rio_txcq_handler(cm, slot); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine46100.00%1100.00%
Total46100.00%1100.00%


static int riocm_queue_req(struct cm_dev *cm, struct rio_dev *rdev, void *buffer, size_t len) { unsigned long flags; struct tx_req *treq; treq = kzalloc(sizeof(*treq), GFP_KERNEL); if (treq == NULL) return -ENOMEM; treq->rdev = rdev; treq->buffer = buffer; treq->len = len; spin_lock_irqsave(&cm->tx_lock, flags); list_add_tail(&treq->node, &cm->tx_reqs); spin_unlock_irqrestore(&cm->tx_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine109100.00%1100.00%
Total109100.00%1100.00%

/* * riocm_post_send - helper function that places packet into msg TX queue * @cm: cm_dev object * @rdev: target RapidIO device object (required by outbound msg interface) * @buffer: pointer to a packet buffer to send * @len: length of data to transfer * @req: request priority flag * * Returns: 0 if success, or error code otherwise. */
static int riocm_post_send(struct cm_dev *cm, struct rio_dev *rdev, void *buffer, size_t len) { int rc; unsigned long flags; spin_lock_irqsave(&cm->tx_lock, flags); if (cm->mport == NULL) { rc = -ENODEV; goto err_out; } if (cm->tx_cnt == RIOCM_TX_RING_SIZE) { riocm_debug(TX, "Tx Queue is full"); rc = -EBUSY; goto err_out; } cm->tx_buf[cm->tx_slot] = buffer; rc = rio_add_outb_message(cm->mport, rdev, cmbox, buffer, len); riocm_debug(TX, "Add buf@%p destid=%x tx_slot=%d tx_cnt=%d", buffer, rdev->destid, cm->tx_slot, cm->tx_cnt); ++cm->tx_cnt; ++cm->tx_slot; cm->tx_slot &= (RIOCM_TX_RING_SIZE - 1); err_out: spin_unlock_irqrestore(&cm->tx_lock, flags); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine167100.00%1100.00%
Total167100.00%1100.00%

/* * riocm_ch_send - sends a data packet to a remote device * @ch_id: local channel ID * @buf: pointer to a data buffer to send (including CM header) * @len: length of data to transfer (including CM header) * * ATTN: ASSUMES THAT THE HEADER SPACE IS RESERVED PART OF THE DATA PACKET * * Returns: 0 if success, or * -EINVAL if one or more input parameters is/are not valid, * -ENODEV if cannot find a channel with specified ID, * -EAGAIN if a channel is not in CONNECTED state, * + error codes returned by HW send routine. */
static int riocm_ch_send(u16 ch_id, void *buf, int len) { struct rio_channel *ch; struct rio_ch_chan_hdr *hdr; int ret; if (buf == NULL || ch_id == 0 || len == 0 || len > RIO_MAX_MSG_SIZE) return -EINVAL; ch = riocm_get_channel(ch_id); if (!ch) { riocm_error("%s(%d) ch_%d not found", current->comm, task_pid_nr(current), ch_id); return -ENODEV; } if (!riocm_cmp(ch, RIO_CM_CONNECTED)) { ret = -EAGAIN; goto err_out; } /* * Fill buffer header section with corresponding channel data */ hdr = buf; hdr->bhdr.src_id = htonl(ch->loc_destid); hdr->bhdr.dst_id = htonl(ch->rem_destid); hdr->bhdr.src_mbox = cmbox; hdr->bhdr.dst_mbox = cmbox; hdr->bhdr.type = RIO_CM_CHAN; hdr->ch_op = CM_DATA_MSG; hdr->dst_ch = htons(ch->rem_channel); hdr->src_ch = htons(ch->id); hdr->msg_len = htons((u16)len); /* ATTN: the function call below relies on the fact that underlying * HW-specific add_outb_message() routine copies TX data into its own * internal transfer buffer (true for all RIONET compatible mport * drivers). Must be reviewed if mport driver uses the buffer directly. */ ret = riocm_post_send(ch->cmdev, ch->rdev, buf, len); if (ret) riocm_debug(TX, "ch %d send_err=%d", ch->id, ret); err_out: riocm_put_channel(ch); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine245100.00%1100.00%
Total245100.00%1100.00%


static int riocm_ch_free_rxbuf(struct rio_channel *ch, void *buf) { int i, ret = -EINVAL; spin_lock_bh(&ch->lock); for (i = 0; i < RIOCM_RX_RING_SIZE; i++) { if (ch->rx_ring.inuse[i] == buf) { ch->rx_ring.inuse[i] = NULL; ch->rx_ring.inuse_cnt--; ret = 0; break; } } spin_unlock_bh(&ch->lock); if (!ret) kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine105100.00%1100.00%
Total105100.00%1100.00%

/* * riocm_ch_receive - fetch a data packet received for the specified channel * @ch: local channel ID * @buf: pointer to a packet buffer * @timeout: timeout to wait for incoming packet (in jiffies) * * Returns: 0 and valid buffer pointer if success, or NULL pointer and one of: * -EAGAIN if a channel is not in CONNECTED state, * -ENOMEM if in-use tracking queue is full, * -ETIME if wait timeout expired, * -EINTR if wait was interrupted. */
static int riocm_ch_receive(struct rio_channel *ch, void **buf, long timeout) { void *rxmsg = NULL; int i, ret = 0; long wret; if (!riocm_cmp(ch, RIO_CM_CONNECTED)) { ret = -EAGAIN; goto out; } if (ch->rx_ring.inuse_cnt == RIOCM_RX_RING_SIZE) { /* If we do not have entries to track buffers given to upper * layer, reject request. */ ret = -ENOMEM; goto out; } wret = wait_for_completion_interruptible_timeout(&ch->comp, timeout); riocm_debug(WAIT, "wait on %d returned %ld", ch->id, wret); if (!wret) ret = -ETIME; else if (wret == -ERESTARTSYS) ret = -EINTR; else ret = riocm_cmp(ch, RIO_CM_CONNECTED) ? 0 : -ECONNRESET; if (ret) goto out; spin_lock_bh(&ch->lock); rxmsg = ch->rx_ring.buf[ch->rx_ring.tail]; ch->rx_ring.buf[ch->rx_ring.tail] = NULL; ch->rx_ring.count--; ch->rx_ring.tail++; ch->rx_ring.tail %= RIOCM_RX_RING_SIZE; ret = -ENOMEM; for (i = 0; i < RIOCM_RX_RING_SIZE; i++) { if (ch->rx_ring.inuse[i] == NULL) { ch->rx_ring.inuse[i] = rxmsg; ch->rx_ring.inuse_cnt++; ret = 0; break; } } if (ret) { /* We have no entry to store pending message: drop it */ kfree(rxmsg); rxmsg = NULL; } spin_unlock_bh(&ch->lock); out: *buf = rxmsg; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine298100.00%1100.00%
Total298100.00%1100.00%

/* * riocm_ch_connect - sends a connect request to a remote device * @loc_ch: local channel ID * @cm: CM device to send connect request * @peer: target RapidIO device * @rem_ch: remote channel ID * * Returns: 0 if success, or * -EINVAL if the channel is not in IDLE state, * -EAGAIN if no connection request available immediately, * -ETIME if ACK response timeout expired, * -EINTR if wait for response was interrupted. */
static int riocm_ch_connect(u16 loc_ch, struct cm_dev *cm, struct cm_peer *peer, u16 rem_ch) { struct rio_channel *ch = NULL; struct rio_ch_chan_hdr *hdr; int ret; long wret; ch = riocm_get_channel(loc_ch); if (!ch) return -ENODEV; if (!riocm_cmp_exch(ch, RIO_CM_IDLE, RIO_CM_CONNECT)) { ret = -EINVAL; goto conn_done; } ch->cmdev = cm; ch->rdev = peer->rdev; ch->context = NULL; ch->loc_destid = cm->mport->host_deviceid; ch->rem_channel = rem_ch; /* * Send connect request to the remote RapidIO device */ hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); if (hdr == NULL) { ret = -ENOMEM; goto conn_done; } hdr->bhdr.src_id = htonl(ch->loc_destid); hdr->bhdr.dst_id = htonl(peer->rdev->destid); hdr->bhdr.src_mbox = cmbox; hdr->bhdr.dst_mbox = cmbox; hdr->bhdr.type = RIO_CM_CHAN; hdr->ch_op = CM_CONN_REQ; hdr->dst_ch = htons(rem_ch); hdr->src_ch = htons(loc_ch); /* ATTN: the function call below relies on the fact that underlying * HW-specific add_outb_message() routine copies TX data into its * internal transfer buffer. Must be reviewed if mport driver uses * this buffer directly. */ ret = riocm_post_send(cm, peer->rdev, hdr, sizeof(*hdr)); if (ret != -EBUSY) { kfree(hdr); } else { ret = riocm_queue_req(cm, peer->rdev, hdr, sizeof(*hdr)); if (ret) kfree(hdr); } if (ret) { riocm_cmp_exch(ch, RIO_CM_CONNECT, RIO_CM_IDLE); goto conn_done; } /* Wait for connect response from the remote device */ wret = wait_for_completion_interruptible_timeout(&ch->comp, RIOCM_CONNECT_TO * HZ); riocm_debug(WAIT, "wait on %d returns %ld", ch->id, wret); if (!wret) ret = -ETIME; else if (wret == -ERESTARTSYS) ret = -EINTR; else ret = riocm_cmp(ch, RIO_CM_CONNECTED) ? 0 : -1; conn_done: riocm_put_channel(ch); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine379100.00%1100.00%
Total379100.00%1100.00%


static int riocm_send_ack(struct rio_channel *ch) { struct rio_ch_chan_hdr *hdr; int ret; hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); if (hdr == NULL) return -ENOMEM; hdr->bhdr.src_id = htonl(ch->loc_destid); hdr->bhdr.dst_id = htonl(ch->rem_destid); hdr->dst_ch = htons(ch->rem_channel); hdr->src_ch = htons(ch->id); hdr->bhdr.src_mbox = cmbox; hdr->bhdr.dst_mbox = cmbox; hdr->bhdr.type = RIO_CM_CHAN; hdr->ch_op = CM_CONN_ACK; /* ATTN: the function call below relies on the fact that underlying * add_outb_message() routine copies TX data into its internal transfer * buffer. Review if switching to direct buffer version. */ ret = riocm_post_send(ch->cmdev, ch->rdev, hdr, sizeof(*hdr)); if (ret == -EBUSY && !riocm_queue_req(ch->cmdev, ch->rdev, hdr, sizeof(*hdr))) return 0; kfree(hdr); if (ret) riocm_error("send ACK to ch_%d on %s failed (ret=%d)", ch->id, rio_name(ch->rdev), ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine202100.00%1100.00%
Total202100.00%1100.00%

/* * riocm_ch_accept - accept incoming connection request * @ch_id: channel ID * @new_ch_id: local mport device * @timeout: wait timeout (if 0 non-blocking call, do not wait if connection * request is not available). * * Returns: pointer to new channel struct if success, or error-valued pointer: * -ENODEV - cannot find specified channel or mport, * -EINVAL - the channel is not in IDLE state, * -EAGAIN - no connection request available immediately (timeout=0), * -ENOMEM - unable to allocate new channel, * -ETIME - wait timeout expired, * -EINTR - wait was interrupted. */
static struct rio_channel *riocm_ch_accept(u16 ch_id, u16 *new_ch_id, long timeout) { struct rio_channel *ch; struct rio_channel *new_ch; struct conn_req *req; struct cm_peer *peer; int found = 0; int err = 0; long wret; ch = riocm_get_channel(ch_id); if (!ch) return ERR_PTR(-EINVAL); if (!riocm_cmp(ch, RIO_CM_LISTEN)) { err = -EINVAL; goto err_put; } /* Don't sleep if this is a non blocking call */ if (!timeout) { if (!try_wait_for_completion(&ch->comp)) { err = -EAGAIN; goto err_put; } } else { riocm_debug(WAIT, "on %d", ch->id); wret = wait_for_completion_interruptible_timeout(&ch->comp, timeout); if (!wret) { err = -ETIME; goto err_put; } else if (wret == -ERESTARTSYS) { err = -EINTR; goto err_put; } } spin_lock_bh(&ch->lock); if (ch->state != RIO_CM_LISTEN) { err = -ECANCELED; } else if (list_empty(&ch->accept_queue)) { riocm_debug(WAIT, "on %d accept_queue is empty on completion", ch->id); err = -EIO; } spin_unlock_bh(&ch->lock); if (err) { riocm_debug(WAIT, "on %d returns %d", ch->id, err); goto err_put; } /* Create new channel for this connection */ new_ch = riocm_ch_alloc(RIOCM_CHNUM_AUTO); if (IS_ERR(new_ch)) { riocm_error("failed to get channel for new req (%ld)", PTR_ERR(new_ch)); err = -ENOMEM; goto err_put; } spin_lock_bh(&ch->lock); req = list_first_entry(&ch->accept_queue, struct conn_req, node); list_del(&req->node); new_ch->cmdev = ch->cmdev; new_ch->loc_destid = ch->loc_destid; new_ch->rem_destid = req->destid; new_ch->rem_channel = req->chan; spin_unlock_bh(&ch->lock); riocm_put_channel(ch); ch = NULL; kfree(req); down_read(&rdev_sem); /* Find requester's device object */ list_for_each_entry(peer, &new_ch->cmdev->peers, node) { if (peer->rdev->destid == new_ch->rem_destid) { riocm_debug(RX_CMD, "found matching device(%s)", rio_name(peer->rdev)); found = 1; break; } } up_read(&rdev_sem); if (!found) { /* If peer device object not found, simply ignore the request */ err = -ENODEV; goto err_put_new_ch; } new_ch->rdev = peer->rdev; new_ch->state = RIO_CM_CONNECTED; spin_lock_init(&new_ch->lock); /* Acknowledge the connection request. */ riocm_send_ack(new_ch); *new_ch_id = new_ch->id; return new_ch; err_put_new_ch: spin_lock_bh(&idr_lock); idr_remove(&ch_idr, new_ch->id); spin_unlock_bh(&idr_lock); riocm_put_channel(new_ch); err_put: if (ch) riocm_put_channel(ch); *new_ch_id = 0; return ERR_PTR(err); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine52496.86%150.00%
Dan Carpenter173.14%150.00%
Total541100.00%2100.00%

/* * riocm_ch_listen - puts a channel into LISTEN state * @ch_id: channel ID * * Returns: 0 if success, or * -EINVAL if the specified channel does not exists or * is not in CHAN_BOUND state. */
static int riocm_ch_listen(u16 ch_id) { struct rio_channel *ch = NULL; int ret = 0; riocm_debug(CHOP, "(ch_%d)", ch_id); ch = riocm_get_channel(ch_id); if (!ch || !riocm_cmp_exch(ch, RIO_CM_CHAN_BOUND, RIO_CM_LISTEN)) ret = -EINVAL; riocm_put_channel(ch); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine65100.00%1100.00%
Total65100.00%1100.00%

/* * riocm_ch_bind - associate a channel object and an mport device * @ch_id: channel ID * @mport_id: local mport device ID * @context: pointer to the additional caller's context * * Returns: 0 if success, or * -ENODEV if cannot find specified mport, * -EINVAL if the specified channel does not exist or * is not in IDLE state. */
static int riocm_ch_bind(u16 ch_id, u8 mport_id, void *context) { struct rio_channel *ch = NULL; struct cm_dev *cm; int rc = -ENODEV; riocm_debug(CHOP, "ch_%d to mport_%d", ch_id, mport_id); /* Find matching cm_dev object */ down_read(&rdev_sem); list_for_each_entry(cm, &cm_dev_list, list) { if ((cm->mport->id == mport_id) && rio_mport_is_running(cm->mport)) { rc = 0; break; } } if (rc) goto exit; ch = riocm_get_channel(ch_id); if (!ch) { rc = -EINVAL; goto exit; } spin_lock_bh(&ch->lock); if (ch->state != RIO_CM_IDLE) { spin_unlock_bh(&ch->lock); rc = -EINVAL; goto err_put; } ch->cmdev = cm; ch->loc_destid = cm->mport->host_deviceid; ch->context = context; ch->state = RIO_CM_CHAN_BOUND; spin_unlock_bh(&ch->lock); err_put: riocm_put_channel(ch); exit: up_read(&rdev_sem); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine205100.00%1100.00%
Total205100.00%1100.00%

/* * riocm_ch_alloc - channel object allocation helper routine * @ch_num: channel ID (1 ... RIOCM_MAX_CHNUM, 0 = automatic) * * Return value: pointer to newly created channel object, * or error-valued pointer */
static struct rio_channel *riocm_ch_alloc(u16 ch_num) { int id; int start, end; struct rio_channel *ch; ch = kzalloc(sizeof(*ch), GFP_KERNEL); if (!ch) return ERR_PTR(-ENOMEM); if (ch_num) { /* If requested, try to obtain the specified channel ID */ start = ch_num; end = ch_num + 1; } else { /* Obtain channel ID from the dynamic allocation range */ start = chstart; end = RIOCM_MAX_CHNUM + 1; } idr_preload(GFP_KERNEL); spin_lock_bh(&idr_lock); id = idr_alloc_cyclic(&ch_idr, ch, start, end, GFP_NOWAIT); spin_unlock_bh(&idr_lock); idr_preload_end(); if (id < 0) { kfree(ch); return ERR_PTR(id == -ENOSPC ? -EBUSY : id); } ch->id = (u16)id; ch->state = RIO_CM_IDLE; spin_lock_init(&ch->lock); INIT_LIST_HEAD(&ch->accept_queue); INIT_LIST_HEAD(&ch->ch_node); init_completion(&ch->comp); init_completion(&ch->comp_close); kref_init(&ch->ref); ch->rx_ring.head = 0; ch->rx_ring.tail = 0; ch->rx_ring.count = 0; ch->rx_ring.inuse_cnt = 0; return ch; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine241100.00%1100.00%
Total241100.00%1100.00%

/* * riocm_ch_create - creates a new channel object and allocates ID for it * @ch_num: channel ID (1 ... RIOCM_MAX_CHNUM, 0 = automatic) * * Allocates and initializes a new channel object. If the parameter ch_num > 0 * and is within the valid range, riocm_ch_create tries to allocate the * specified ID for the new channel. If ch_num = 0, channel ID will be assigned * automatically from the range (chstart ... RIOCM_MAX_CHNUM). * Module parameter 'chstart' defines start of an ID range available for dynamic * allocation. Range below 'chstart' is reserved for pre-defined ID numbers. * Available channel numbers are limited by 16-bit size of channel numbers used * in the packet header. * * Return value: PTR to rio_channel structure if successful (with channel number * updated via pointer) or error-valued pointer if error. */
static struct rio_channel *riocm_ch_create(u16 *ch_num) { struct rio_channel *ch = NULL; ch = riocm_ch_alloc(*ch_num); if (IS_ERR(ch)) riocm_debug(CHOP, "Failed to allocate channel %d (err=%ld)", *ch_num, PTR_ERR(ch)); else *ch_num = ch->id; return ch; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine60100.00%1100.00%
Total60100.00%1100.00%

/* * riocm_ch_free - channel object release routine * @ref: pointer to a channel's kref structure */
static void riocm_ch_free(struct kref *ref) { struct rio_channel *ch = container_of(ref, struct rio_channel, ref); int i; riocm_debug(CHOP, "(ch_%d)", ch->id); if (ch->rx_ring.inuse_cnt) { for (i = 0; i < RIOCM_RX_RING_SIZE && ch->rx_ring.inuse_cnt; i++) { if (ch->rx_ring.inuse[i] != NULL) { kfree(ch->rx_ring.inuse[i]); ch->rx_ring.inuse_cnt--; } } } if (ch->rx_ring.count) for (i = 0; i < RIOCM_RX_RING_SIZE && ch->rx_ring.count; i++) { if (ch->rx_ring.buf[i] != NULL) { kfree(ch->rx_ring.buf[i]); ch->rx_ring.count--; } } complete(&ch->comp_close); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine176100.00%1100.00%
Total176100.00%1100.00%


static int riocm_send_close(struct rio_channel *ch) { struct rio_ch_chan_hdr *hdr; int ret; /* * Send CH_CLOSE notification to the remote RapidIO device */ hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); if (hdr == NULL) return -ENOMEM; hdr->bhdr.src_id = htonl(ch->loc_destid); hdr->bhdr.dst_id = htonl(ch->rem_destid); hdr->bhdr.src_mbox = cmbox; hdr->bhdr.dst_mbox = cmbox; hdr->bhdr.type = RIO_CM_CHAN; hdr->ch_op = CM_CONN_CLOSE; hdr->dst_ch = htons(ch->rem_channel); hdr->src_ch = htons(ch->id); /* ATTN: the function call below relies on the fact that underlying * add_outb_message() routine copies TX data into its internal transfer * buffer. Needs to be reviewed if switched to direct buffer mode. */ ret = riocm_post_send(ch->cmdev, ch->rdev, hdr, sizeof(*hdr)); if (ret == -EBUSY && !riocm_queue_req(ch->cmdev, ch->rdev, hdr, sizeof(*hdr))) return 0; kfree(hdr); if (ret) riocm_error("ch(%d) send CLOSE failed (ret=%d)", ch->id, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine196100.00%1100.00%
Total196100.00%1100.00%

/* * riocm_ch_close - closes a channel object with specified ID (by local request) * @ch: channel to be closed */
static int riocm_ch_close(struct rio_channel *ch) { unsigned long tmo = msecs_to_jiffies(3000); enum rio_cm_state state; long wret; int ret = 0; riocm_debug(CHOP, "ch_%d by %s(%d)", ch->id, current->comm, task_pid_nr(current)); state = riocm_exch(ch, RIO_CM_DESTROYING); if (state == RIO_CM_CONNECTED) riocm_send_close(ch); complete_all(&ch->comp); riocm_put_channel(ch); wret = wait_for_completion_interruptible_timeout(&ch->comp_close, tmo); riocm_debug(WAIT, "wait on %d returns %ld", ch->id, wret); if (wret == 0) { /* Timeout on wait occurred */ riocm_debug(CHOP, "%s(%d) timed out waiting for ch %d", current->comm, task_pid_nr(current), ch->id); ret = -ETIMEDOUT; } else if (wret == -ERESTARTSYS) { /* Wait_for_completion was interrupted by a signal */ riocm_debug(CHOP, "%s(%d) wait for ch %d was interrupted", current->comm, task_pid_nr(current), ch->id); ret = -EINTR; } if (!ret) { riocm_debug(CHOP, "ch_%d resources released", ch->id); kfree(ch); } else { riocm_debug(CHOP, "failed to release ch_%d resources", ch->id); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine220100.00%1100.00%
Total220100.00%1100.00%

/* * riocm_cdev_open() - Open character device */
static int riocm_cdev_open(struct inode *inode, struct file *filp) { riocm_debug(INIT, "by %s(%d) filp=%p ", current->comm, task_pid_nr(current), filp); if (list_empty(&cm_dev_list)) return -ENODEV; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine49100.00%1100.00%
Total49100.00%1100.00%

/* * riocm_cdev_release() - Release character device */
static int riocm_cdev_release(struct inode *inode, struct file *filp) { struct rio_channel *ch, *_c; unsigned int i; LIST_HEAD(list); riocm_debug(EXIT, "by %s(%d) filp=%p", current->comm, task_pid_nr(current), filp); /* Check if there are channels associated with this file descriptor */ spin_lock_bh(&idr_lock); idr_for_each_entry(&ch_idr, ch, i) { if (ch && ch->filp == filp) { riocm_debug(EXIT, "ch_%d not released by %s(%d)", ch->id, current->comm, task_pid_nr(current)); idr_remove(&ch_idr, ch->id); list_add(&ch->ch_node, &list); } } spin_unlock_bh(&idr_lock); if (!list_empty(&list)) { list_for_each_entry_safe(ch, _c, &list, ch_node) { list_del(&ch->ch_node); riocm_ch_close(ch); } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine166100.00%1100.00%
Total166100.00%1100.00%

/* * cm_ep_get_list_size() - Reports number of endpoints in the network */
static int cm_ep_get_list_size(void __user *arg) { u32 __user *p = arg; u32 mport_id; u32 count = 0; struct cm_dev *cm; if (get_user(mport_id, p)) return -EFAULT; if (mport_id >= RIO_MAX_MPORTS) return -EINVAL; /* Find a matching cm_dev object */ down_read(&rdev_sem); list_for_each_entry(cm, &cm_dev_list, list) { if (cm->mport->id == mport_id) { count = cm->npeers; up_read(&rdev_sem); if (copy_to_user(arg, &count, sizeof(u32))) return -EFAULT; return 0; } } up_read(&rdev_sem); return -ENODEV; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine127100.00%1100.00%
Total127100.00%1100.00%

/* * cm_ep_get_list() - Returns list of attached endpoints */
static int cm_ep_get_list(void __user *arg) { struct cm_dev *cm; struct cm_peer *peer; u32 info[2]; void *buf; u32 nent; u32 *entry_ptr; u32 i = 0; int ret = 0; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; if (info[1] >= RIO_MAX_MPORTS || info[0] > RIOCM_MAX_EP_COUNT) return -EINVAL; /* Find a matching cm_dev object */ down_read(&rdev_sem); list_for_each_entry(cm, &cm_dev_list, list) if (cm->mport->id == (u8)info[1]) goto found; up_read(&rdev_sem); return -ENODEV; found: nent = min(info[0], cm->npeers); buf = kcalloc(nent + 2, sizeof(u32), GFP_KERNEL); if (!buf) { up_read(&rdev_sem); return -ENOMEM; } entry_ptr = (u32 *)((uintptr_t)buf + 2*sizeof(u32)); list_for_each_entry(peer, &cm->peers, node) { *entry_ptr = (u32)peer->rdev->destid; entry_ptr++; if (++i == nent) break; } up_read(&rdev_sem); ((u32 *)buf)[0] = i; /* report an updated number of entries */ ((u32 *)buf)[1] = info[1]; /* put back an mport ID */ if (copy_to_user(arg, buf, sizeof(u32) * (info[0] + 2))) ret = -EFAULT; kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine306100.00%1100.00%
Total306100.00%1100.00%

/* * cm_mport_get_list() - Returns list of available local mport devices */
static int cm_mport_get_list(void __user *arg) { int ret = 0; u32 entries; void *buf; struct cm_dev *cm; u32 *entry_ptr; int count = 0; if (copy_from_user(&entries, arg, sizeof(entries))) return -EFAULT; if (entries == 0 || entries > RIO_MAX_MPORTS) return -EINVAL; buf = kcalloc(entries + 1, sizeof(u32), GFP_KERNEL); if (!buf) return -ENOMEM; /* Scan all registered cm_dev objects */ entry_ptr = (u32 *)((uintptr_t)buf + sizeof(u32)); down_read(&rdev_sem); list_for_each_entry(cm, &cm_dev_list, list) { if (count++ < entries) { *entry_ptr = (cm->mport->id << 16) | cm->mport->host_deviceid; entry_ptr++; } } up_read(&rdev_sem); *((u32 *)buf) = count; /* report a real number of entries */ if (copy_to_user(arg, buf, sizeof(u32) * (count + 1))) ret = -EFAULT; kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine212100.00%1100.00%
Total212100.00%1100.00%

/* * cm_chan_create() - Create a message exchange channel */
static int cm_chan_create(struct file *filp, void __user *arg) { u16 __user *p = arg; u16 ch_num; struct rio_channel *ch; if (get_user(ch_num, p)) return -EFAULT; riocm_debug(CHOP, "ch_%d requested by %s(%d)", ch_num, current->comm, task_pid_nr(current)); ch = riocm_ch_create(&ch_num); if (IS_ERR(ch)) return PTR_ERR(ch); ch->filp = filp; riocm_debug(CHOP, "ch_%d created by %s(%d)", ch_num, current->comm, task_pid_nr(current)); return put_user(ch_num, p); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine115100.00%1100.00%
Total115100.00%1100.00%

/* * cm_chan_close() - Close channel * @filp: Pointer to file object * @arg: Channel to close */
static int cm_chan_close(struct file *filp, void __user *arg) { u16 __user *p = arg; u16 ch_num; struct rio_channel *ch; if (get_user(ch_num, p)) return -EFAULT; riocm_debug(CHOP, "ch_%d by %s(%d)", ch_num, current->comm, task_pid_nr(current)); spin_lock_bh(&idr_lock); ch = idr_find(&ch_idr, ch_num); if (!ch) { spin_unlock_bh(&idr_lock); return 0; } if (ch->filp != filp) { spin_unlock_bh(&idr_lock); return -EINVAL; } idr_remove(&ch_idr, ch->id); spin_unlock_bh(&idr_lock); return riocm_ch_close(ch); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine136100.00%1100.00%
Total136100.00%1100.00%

/* * cm_chan_bind() - Bind channel * @arg: Channel number */
static int cm_chan_bind(void __user *arg) { struct rio_cm_channel chan; if (copy_from_user(&chan, arg, sizeof(chan))) return -EFAULT; if (chan.mport_id >= RIO_MAX_MPORTS) return -EINVAL; return riocm_ch_bind(chan.id, chan.mport_id, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine60100.00%1100.00%
Total60100.00%1100.00%

/* * cm_chan_listen() - Listen on channel * @arg: Channel number */
static int cm_chan_listen(void __user *arg) { u16 __user *p = arg; u16 ch_num; if (get_user(ch_num, p)) return -EFAULT; return riocm_ch_listen(ch_num); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine40100.00%1100.00%
Total40100.00%1100.00%

/* * cm_chan_accept() - Accept incoming connection * @filp: Pointer to file object * @arg: Channel number */
static int cm_chan_accept(struct file *filp, void __user *arg) { struct rio_cm_accept param; long accept_to; struct rio_channel *ch; if (copy_from_user(&param, arg, sizeof(param))) return -EFAULT; riocm_debug(CHOP, "on ch_%d by %s(%d)", param.ch_num, current->comm, task_pid_nr(current)); accept_to = param.wait_to ? msecs_to_jiffies(param.wait_to) : 0; ch = riocm_ch_accept(param.ch_num, &param.ch_num, accept_to); if (IS_ERR(ch)) return PTR_ERR(ch); ch->filp = filp; riocm_debug(CHOP, "new ch_%d for %s(%d)", ch->id, current->comm, task_pid_nr(current)); if (copy_to_user(arg, &param, sizeof(param))) return -EFAULT; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine159100.00%1100.00%
Total159100.00%1100.00%

/* * cm_chan_connect() - Connect on channel * @arg: Channel information */
static int cm_chan_connect(void __user *arg) { struct rio_cm_channel chan; struct cm_dev *cm; struct cm_peer *peer; int ret = -ENODEV; if (copy_from_user(&chan, arg, sizeof(chan))) return -EFAULT; if (chan.mport_id >= RIO_MAX_MPORTS) return -EINVAL; down_read(&rdev_sem); /* Find matching cm_dev object */ list_for_each_entry(cm, &cm_dev_list, list) { if (cm->mport->id == chan.mport_id) { ret = 0; break; } } if (ret) goto err_out; if (chan.remote_destid >= RIO_ANY_DESTID(cm->mport->sys_size)) { ret = -EINVAL; goto err_out; } /* Find corresponding RapidIO endpoint device object */ ret = -ENODEV; list_for_each_entry(peer, &cm->peers, node) { if (peer->rdev->destid == chan.remote_destid) { ret = 0; break; } } if (ret) goto err_out; up_read(&rdev_sem); return riocm_ch_connect(chan.id, cm, peer, chan.remote_channel); err_out: up_read(&rdev_sem); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine205100.00%1100.00%
Total205100.00%1100.00%

/* * cm_chan_msg_send() - Send a message through channel * @arg: Outbound message information */
static int cm_chan_msg_send(void __user *arg) { struct rio_cm_msg msg; void *buf; int ret; if (copy_from_user(&msg, arg, sizeof(msg))) return -EFAULT; if (msg.size > RIO_MAX_MSG_SIZE) return -EINVAL; buf = memdup_user((void __user *)(uintptr_t)msg.msg, msg.size); if (IS_ERR(buf)) return PTR_ERR(buf); ret = riocm_ch_send(msg.ch_num, buf, msg.size); kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine110100.00%2100.00%
Total110100.00%2100.00%

/* * cm_chan_msg_rcv() - Receive a message through channel * @arg: Inbound message information */
static int cm_chan_msg_rcv(void __user *arg) { struct rio_cm_msg msg; struct rio_channel *ch; void *buf; long rxto; int ret = 0, msg_size; if (copy_from_user(&msg, arg, sizeof(msg))) return -EFAULT; if (msg.ch_num == 0 || msg.size == 0) return -EINVAL; ch = riocm_get_channel(msg.ch_num); if (!ch) return -ENODEV; rxto = msg.rxto ? msecs_to_jiffies(msg.rxto) : MAX_SCHEDULE_TIMEOUT; ret = riocm_ch_receive(ch, &buf, rxto); if (ret) goto out; msg_size = min(msg.size, (u16)(RIO_MAX_MSG_SIZE)); if (copy_to_user((void __user *)(uintptr_t)msg.msg, buf, msg_size)) ret = -EFAULT; riocm_ch_free_rxbuf(ch, buf); out: riocm_put_channel(ch); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine182100.00%1100.00%
Total182100.00%1100.00%

/* * riocm_cdev_ioctl() - IOCTL requests handler */
static long riocm_cdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { case RIO_CM_EP_GET_LIST_SIZE: return cm_ep_get_list_size((void __user *)arg); case RIO_CM_EP_GET_LIST: return cm_ep_get_list((void __user *)arg); case RIO_CM_CHAN_CREATE: return cm_chan_create(filp, (void __user *)arg); case RIO_CM_CHAN_CLOSE: return cm_chan_close(filp, (void __user *)arg); case RIO_CM_CHAN_BIND: return cm_chan_bind((void __user *)arg); case RIO_CM_CHAN_LISTEN: return cm_chan_listen((void __user *)arg); case RIO_CM_CHAN_ACCEPT: return cm_chan_accept(filp, (void __user *)arg); case RIO_CM_CHAN_CONNECT: return cm_chan_connect((void __user *)arg); case RIO_CM_CHAN_SEND: return cm_chan_msg_send((void __user *)arg); case RIO_CM_CHAN_RECEIVE: return cm_chan_msg_rcv((void __user *)arg); case RIO_CM_MPORT_GET_LIST: return cm_mport_get_list((void __user *)arg); default: break; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine191100.00%1100.00%
Total191100.00%1100.00%

static const struct file_operations riocm_cdev_fops = { .owner = THIS_MODULE, .open = riocm_cdev_open, .release = riocm_cdev_release, .unlocked_ioctl = riocm_cdev_ioctl, }; /* * riocm_add_dev - add new remote RapidIO device into channel management core * @dev: device object associated with RapidIO device * @sif: subsystem interface * * Adds the specified RapidIO device (if applicable) into peers list of * the corresponding channel management device (cm_dev). */
static int riocm_add_dev(struct device *dev, struct subsys_interface *sif) { struct cm_peer *peer; struct rio_dev *rdev = to_rio_dev(dev); struct cm_dev *cm; /* Check if the remote device has capabilities required to support CM */ if (!dev_cm_capable(rdev)) return 0; riocm_debug(RDEV, "(%s)", rio_name(rdev)); peer = kmalloc(sizeof(*peer), GFP_KERNEL); if (!peer) return -ENOMEM; /* Find a corresponding cm_dev object */ down_write(&rdev_sem); list_for_each_entry(cm, &cm_dev_list, list) { if (cm->mport == rdev->net->hport) goto found; } up_write(&rdev_sem); kfree(peer); return -ENODEV; found: peer->rdev = rdev; list_add_tail(&peer->node, &cm->peers); cm->npeers++; up_write(&rdev_sem); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine164100.00%1100.00%
Total164100.00%1100.00%

/* * riocm_remove_dev - remove remote RapidIO device from channel management core * @dev: device object associated with RapidIO device * @sif: subsystem interface * * Removes the specified RapidIO device (if applicable) from peers list of * the corresponding channel management device (cm_dev). */
static void riocm_remove_dev(struct device *dev, struct subsys_interface *sif) { struct rio_dev *rdev = to_rio_dev(dev); struct cm_dev *cm; struct cm_peer *peer; struct rio_channel *ch, *_c; unsigned int i; bool found = false; LIST_HEAD(list); /* Check if the remote device has capabilities required to support CM */ if (!dev_cm_capable(rdev)) return; riocm_debug(RDEV, "(%s)", rio_name(rdev)); /* Find matching cm_dev object */ down_write(&rdev_sem); list_for_each_entry(cm, &cm_dev_list, list) { if (cm->mport == rdev->net->hport) { found = true; break; } } if (!found) { up_write(&rdev_sem); return; } /* Remove remote device from the list of peers */ found = false; list_for_each_entry(peer, &cm->peers, node) { if (peer->rdev == rdev) { riocm_debug(RDEV, "removing peer %s", rio_name(rdev)); found = true; list_del(&peer->node); cm->npeers--; kfree(peer); break; } } up_write(&rdev_sem); if (!found) return; /* * Release channels associated with this peer */ spin_lock_bh(&idr_lock); idr_for_each_entry(&ch_idr, ch, i) { if (ch && ch->rdev == rdev) { if (atomic_read(&rdev->state) != RIO_DEVICE_SHUTDOWN) riocm_exch(ch, RIO_CM_DISCONNECT); idr_remove(&ch_idr, ch->id); list_add(&ch->ch_node, &list); } } spin_unlock_bh(&idr_lock); if (!list_empty(&list)) { list_for_each_entry_safe(ch, _c, &list, ch_node) { list_del(&ch->ch_node); riocm_ch_close(ch); } } }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine313100.00%1100.00%
Total313100.00%1100.00%

/* * riocm_cdev_add() - Create rio_cm char device * @devno: device number assigned to device (MAJ + MIN) */
static int riocm_cdev_add(dev_t devno) { int ret; cdev_init(&riocm_cdev.cdev, &riocm_cdev_fops); riocm_cdev.cdev.owner = THIS_MODULE; ret = cdev_add(&riocm_cdev.cdev, devno, 1); if (ret < 0) { riocm_error("Cannot register a device with error %d", ret); return ret; } riocm_cdev.dev = device_create(dev_class, NULL, devno, NULL, DEV_NAME); if (IS_ERR(riocm_cdev.dev)) { cdev_del(&riocm_cdev.cdev); return PTR_ERR(riocm_cdev.dev); } riocm_debug(MPORT, "Added %s cdev(%d:%d)", DEV_NAME, MAJOR(devno), MINOR(devno)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine129100.00%1100.00%
Total129100.00%1100.00%

/* * riocm_add_mport - add new local mport device into channel management core * @dev: device object associated with mport * @class_intf: class interface * * When a new mport device is added, CM immediately reserves inbound and * outbound RapidIO mailboxes that will be used. */
static int riocm_add_mport(struct device *dev, struct class_interface *class_intf) { int rc; int i; struct cm_dev *cm; struct rio_mport *mport = to_rio_mport(dev); riocm_debug(MPORT, "add mport %s", mport->name); cm = kzalloc(sizeof(*cm), GFP_KERNEL); if (!cm) return -ENOMEM; cm->mport = mport; rc = rio_request_outb_mbox(mport, cm, cmbox, RIOCM_TX_RING_SIZE, riocm_outb_msg_event); if (rc) { riocm_error("failed to allocate OBMBOX_%d on %s", cmbox, mport->name); kfree(cm); return -ENODEV; } rc = rio_request_inb_mbox(mport, cm, cmbox, RIOCM_RX_RING_SIZE, riocm_inb_msg_event); if (rc) { riocm_error("failed to allocate IBMBOX_%d on %s", cmbox, mport->name); rio_release_outb_mbox(mport, cmbox); kfree(cm); return -ENODEV; } /* * Allocate and register inbound messaging buffers to be ready * to receive channel and system management requests */ for (i = 0; i < RIOCM_RX_RING_SIZE; i++) cm->rx_buf[i] = NULL; cm->rx_slots = RIOCM_RX_RING_SIZE; mutex_init(&cm->rx_lock); riocm_rx_fill(cm, RIOCM_RX_RING_SIZE); cm->rx_wq = create_workqueue(DRV_NAME "/rxq"); INIT_WORK(&cm->rx_work, rio_ibmsg_handler); cm->tx_slot = 0; cm->tx_cnt = 0; cm->tx_ack_slot = 0; spin_lock_init(&cm->tx_lock); INIT_LIST_HEAD(&cm->peers); cm->npeers = 0; INIT_LIST_HEAD(&cm->tx_reqs); down_write(&rdev_sem); list_add_tail(&cm->list, &cm_dev_list); up_write(&rdev_sem); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine303100.00%1100.00%
Total303100.00%1100.00%

/* * riocm_remove_mport - remove local mport device from channel management core * @dev: device object associated with mport * @class_intf: class interface * * Removes a local mport device from the list of registered devices that provide * channel management services. Returns an error if the specified mport is not * registered with the CM core. */
static void riocm_remove_mport(struct device *dev, struct class_interface *class_intf) { struct rio_mport *mport = to_rio_mport(dev); struct cm_dev *cm; struct cm_peer *peer, *temp; struct rio_channel *ch, *_c; unsigned int i; bool found = false; LIST_HEAD(list); riocm_debug(MPORT, "%s", mport->name); /* Find a matching cm_dev object */ down_write(&rdev_sem); list_for_each_entry(cm, &cm_dev_list, list) { if (cm->mport == mport) { list_del(&cm->list); found = true; break; } } up_write(&rdev_sem); if (!found) return; flush_workqueue(cm->rx_wq); destroy_workqueue(cm->rx_wq); /* Release channels bound to this mport */ spin_lock_bh(&idr_lock); idr_for_each_entry(&ch_idr, ch, i) { if (ch->cmdev == cm) { riocm_debug(RDEV, "%s drop ch_%d", mport->name, ch->id); idr_remove(&ch_idr, ch->id); list_add(&ch->ch_node, &list); } } spin_unlock_bh(&idr_lock); if (!list_empty(&list)) { list_for_each_entry_safe(ch, _c, &list, ch_node) { list_del(&ch->ch_node); riocm_ch_close(ch); } } rio_release_inb_mbox(mport, cmbox); rio_release_outb_mbox(mport, cmbox); /* Remove and free peer entries */ if (!list_empty(&cm->peers)) riocm_debug(RDEV, "ATTN: peer list not empty"); list_for_each_entry_safe(peer, temp, &cm->peers, node) { riocm_debug(RDEV, "removing peer %s", rio_name(peer->rdev)); list_del(&peer->node); kfree(peer); } riocm_rx_free(cm); kfree(cm); riocm_debug(MPORT, "%s done", mport->name); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine336100.00%1100.00%
Total336100.00%1100.00%


static int rio_cm_shutdown(struct notifier_block *nb, unsigned long code, void *unused) { struct rio_channel *ch; unsigned int i; LIST_HEAD(list); riocm_debug(EXIT, "."); /* * If there are any channels left in connected state send * close notification to the connection partner. * First build a list of channels that require a closing * notification because function riocm_send_close() should * be called outside of spinlock protected code. */ spin_lock_bh(&idr_lock); idr_for_each_entry(&ch_idr, ch, i) { if (ch->state == RIO_CM_CONNECTED) { riocm_debug(EXIT, "close ch %d", ch->id); idr_remove(&ch_idr, ch->id); list_add(&ch->ch_node, &list); } } spin_unlock_bh(&idr_lock); list_for_each_entry(ch, &list, ch_node) riocm_send_close(ch); return NOTIFY_DONE; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine121100.00%2100.00%
Total121100.00%2100.00%

/* * riocm_interface handles addition/removal of remote RapidIO devices */ static struct subsys_interface riocm_interface = { .name = "rio_cm", .subsys = &rio_bus_type, .add_dev = riocm_add_dev, .remove_dev = riocm_remove_dev, }; /* * rio_mport_interface handles addition/removal local mport devices */ static struct class_interface rio_mport_interface __refdata = { .class = &rio_mport_class, .add_dev = riocm_add_mport, .remove_dev = riocm_remove_mport, }; static struct notifier_block rio_cm_notifier = { .notifier_call = rio_cm_shutdown, };
static int __init riocm_init(void) { int ret; /* Create device class needed by udev */ dev_class = class_create(THIS_MODULE, DRV_NAME); if (IS_ERR(dev_class)) { riocm_error("Cannot create " DRV_NAME " class"); return PTR_ERR(dev_class); } ret = alloc_chrdev_region(&dev_number, 0, 1, DRV_NAME); if (ret) { class_destroy(dev_class); return ret; } dev_major = MAJOR(dev_number); dev_minor_base = MINOR(dev_number); riocm_debug(INIT, "Registered class with %d major", dev_major); /* * Register as rapidio_port class interface to get notifications about * mport additions and removals. */ ret = class_interface_register(&rio_mport_interface); if (ret) { riocm_error("class_interface_register error: %d", ret); goto err_reg; } /* * Register as RapidIO bus interface to get notifications about * addition/removal of remote RapidIO devices. */ ret = subsys_interface_register(&riocm_interface); if (ret) { riocm_error("subsys_interface_register error: %d", ret); goto err_cl; } ret = register_reboot_notifier(&rio_cm_notifier); if (ret) { riocm_error("failed to register reboot notifier (err=%d)", ret); goto err_sif; } ret = riocm_cdev_add(dev_number); if (ret) { unregister_reboot_notifier(&rio_cm_notifier); ret = -ENODEV; goto err_sif; } return 0; err_sif: subsys_interface_unregister(&riocm_interface); err_cl: class_interface_unregister(&rio_mport_interface); err_reg: unregister_chrdev_region(dev_number, 1); class_destroy(dev_class); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine232100.00%1100.00%
Total232100.00%1100.00%


static void __exit riocm_exit(void) { riocm_debug(EXIT, "enter"); unregister_reboot_notifier(&rio_cm_notifier); subsys_interface_unregister(&riocm_interface); class_interface_unregister(&rio_mport_interface); idr_destroy(&ch_idr); device_unregister(riocm_cdev.dev); cdev_del(&(riocm_cdev.cdev)); class_destroy(dev_class); unregister_chrdev_region(dev_number, 1); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine69100.00%1100.00%
Total69100.00%1100.00%

late_initcall(riocm_init); module_exit(riocm_exit);

Overall Contributors

PersonTokensPropCommitsCommitProp
Alexandre Bounine971199.83%375.00%
Dan Carpenter170.17%125.00%
Total9728100.00%4100.00%
Directory: drivers/rapidio
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.