#include <linux/ntb.h>
#include "ntb_hw.h"
-#define NTB_TRANSPORT_VERSION 1
+#define NTB_TRANSPORT_VERSION 2
-static int transport_mtu = 0x401E;
+static unsigned int transport_mtu = 0x401E;
module_param(transport_mtu, uint, 0644);
MODULE_PARM_DESC(transport_mtu, "Maximum size of NTB transport packets");
unsigned int flags;
};
+struct ntb_rx_info {
+ unsigned int entry;
+};
+
struct ntb_transport_qp {
struct ntb_transport *transport;
struct ntb_device *ndev;
bool qp_link;
u8 qp_num; /* Only 64 QP's are allowed. 0-63 */
+ struct ntb_rx_info __iomem *rx_info;
+ struct ntb_rx_info *remote_rx_info;
+
void (*tx_handler) (struct ntb_transport_qp *qp, void *qp_data,
void *data, int len);
struct list_head tx_free_q;
spinlock_t ntb_tx_free_q_lock;
- void *tx_mw_begin;
- void *tx_mw_end;
- void *tx_offset;
+ void __iomem *tx_mw;
+ unsigned int tx_index;
+ unsigned int tx_max_entry;
+ unsigned int tx_max_frame;
void (*rx_handler) (struct ntb_transport_qp *qp, void *qp_data,
void *data, int len);
struct list_head rx_free_q;
spinlock_t ntb_rx_pend_q_lock;
spinlock_t ntb_rx_free_q_lock;
- void *rx_buff_begin;
- void *rx_buff_end;
- void *rx_offset;
+ void *rx_buff;
+ unsigned int rx_index;
+ unsigned int rx_max_entry;
+ unsigned int rx_max_frame;
void (*event_handler) (void *data, int status);
struct delayed_work link_work;
+ struct work_struct link_cleanup;
struct dentry *debugfs_dir;
struct dentry *debugfs_stats;
unsigned long qp_bitmap;
bool transport_link;
struct delayed_work link_work;
+ struct work_struct link_cleanup;
struct dentry *debugfs_dir;
};
};
struct ntb_payload_header {
- u64 ver;
+ unsigned int ver;
unsigned int len;
unsigned int flags;
};
return 0;
}
-struct bus_type ntb_bus_type = {
+static struct bus_type ntb_bus_type = {
.name = "ntb_bus",
.match = ntb_match_bus,
.probe = ntb_client_probe,
static LIST_HEAD(ntb_transport_list);
-static int __devinit ntb_bus_init(struct ntb_transport *nt)
+static int ntb_bus_init(struct ntb_transport *nt)
{
if (list_empty(&ntb_transport_list)) {
int rc = bus_register(&ntb_bus_type);
return 0;
}
-static void __devexit ntb_bus_remove(struct ntb_transport *nt)
+static void ntb_bus_remove(struct ntb_transport *nt)
{
struct ntb_transport_client_dev *client_dev, *cd;
struct ntb_transport *nt;
int rc;
+ if (list_empty(&ntb_transport_list))
+ return -ENODEV;
+
list_for_each_entry(nt, &ntb_transport_list, entry) {
struct device *dev;
{
drv->driver.bus = &ntb_bus_type;
+ if (list_empty(&ntb_transport_list))
+ return -ENODEV;
+
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(ntb_register_client);
}
EXPORT_SYMBOL_GPL(ntb_unregister_client);
-static int debugfs_open(struct inode *inode, struct file *filp)
-{
- filp->private_data = inode->i_private;
- return 0;
-}
-
static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count,
loff_t *offp)
{
struct ntb_transport_qp *qp;
- char buf[1024];
+ char *buf;
ssize_t ret, out_offset, out_count;
- out_count = 1024;
+ out_count = 600;
+
+ buf = kmalloc(out_count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
qp = filp->private_data;
out_offset = 0;
out_offset += snprintf(buf + out_offset, out_count - out_offset,
"rx_err_ver - \t%llu\n", qp->rx_err_ver);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
- "rx_buff_begin - %p\n", qp->rx_buff_begin);
+ "rx_buff - \t%p\n", qp->rx_buff);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
- "rx_offset - \t%p\n", qp->rx_offset);
+ "rx_index - \t%u\n", qp->rx_index);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
- "rx_buff_end - \t%p\n", qp->rx_buff_end);
+ "rx_max_entry - \t%u\n", qp->rx_max_entry);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
"tx_bytes - \t%llu\n", qp->tx_bytes);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
"tx_ring_full - \t%llu\n", qp->tx_ring_full);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
- "tx_mw_begin - \t%p\n", qp->tx_mw_begin);
+ "tx_mw - \t%p\n", qp->tx_mw);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
- "tx_offset - \t%p\n", qp->tx_offset);
+ "tx_index - \t%u\n", qp->tx_index);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
- "tx_mw_end - \t%p\n", qp->tx_mw_end);
+ "tx_max_entry - \t%u\n", qp->tx_max_entry);
out_offset += snprintf(buf + out_offset, out_count - out_offset,
- "QP Link %s\n", (qp->qp_link == NTB_LINK_UP) ?
+ "\nQP Link %s\n", (qp->qp_link == NTB_LINK_UP) ?
"Up" : "Down");
+ if (out_offset > out_count)
+ out_offset = out_count;
ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset);
+ kfree(buf);
return ret;
}
static const struct file_operations ntb_qp_debugfs_stats = {
.owner = THIS_MODULE,
- .open = debugfs_open,
+ .open = simple_open,
.read = debugfs_read,
};
unsigned int qp_num)
{
struct ntb_transport_qp *qp = &nt->qps[qp_num];
- unsigned int size, num_qps_mw;
+ unsigned int rx_size, num_qps_mw;
u8 mw_num = QP_TO_MW(qp_num);
+ unsigned int i;
- WARN_ON(nt->mw[mw_num].virt_addr == 0);
+ WARN_ON(nt->mw[mw_num].virt_addr == NULL);
- if (nt->max_qps % NTB_NUM_MW && !mw_num)
- num_qps_mw = nt->max_qps / NTB_NUM_MW +
- (nt->max_qps % NTB_NUM_MW - mw_num);
+ if (nt->max_qps % NTB_NUM_MW && mw_num < nt->max_qps % NTB_NUM_MW)
+ num_qps_mw = nt->max_qps / NTB_NUM_MW + 1;
else
num_qps_mw = nt->max_qps / NTB_NUM_MW;
- size = nt->mw[mw_num].size / num_qps_mw;
+ rx_size = (unsigned int) nt->mw[mw_num].size / num_qps_mw;
+ qp->remote_rx_info = nt->mw[mw_num].virt_addr +
+ (qp_num / NTB_NUM_MW * rx_size);
+ rx_size -= sizeof(struct ntb_rx_info);
+
+ qp->rx_buff = qp->remote_rx_info + sizeof(struct ntb_rx_info);
+ qp->rx_max_frame = min(transport_mtu, rx_size);
+ qp->rx_max_entry = rx_size / qp->rx_max_frame;
+ qp->rx_index = 0;
- qp->rx_buff_begin = nt->mw[mw_num].virt_addr +
- (qp_num / NTB_NUM_MW * size);
- qp->rx_buff_end = qp->rx_buff_begin + size;
- qp->rx_offset = qp->rx_buff_begin;
+ qp->remote_rx_info->entry = qp->rx_max_entry;
- qp->tx_mw_begin = ntb_get_mw_vbase(nt->ndev, mw_num) +
- (qp_num / NTB_NUM_MW * size);
- qp->tx_mw_end = qp->tx_mw_begin + size;
- qp->tx_offset = qp->tx_mw_begin;
+ /* setup the hdr offsets with 0's */
+ for (i = 0; i < qp->rx_max_entry; i++) {
+ void *offset = qp->rx_buff + qp->rx_max_frame * (i + 1) -
+ sizeof(struct ntb_payload_header);
+ memset(offset, 0, sizeof(struct ntb_payload_header));
+ }
qp->rx_pkts = 0;
qp->tx_pkts = 0;
{
struct ntb_transport_mw *mw = &nt->mw[num_mw];
struct pci_dev *pdev = ntb_query_pdev(nt->ndev);
- void *offset;
/* Alloc memory for receiving data. Must be 4k aligned */
mw->size = ALIGN(size, 4096);
return -ENOMEM;
}
- /* setup the hdr offsets with 0's */
- for (offset = mw->virt_addr + transport_mtu -
- sizeof(struct ntb_payload_header);
- offset < mw->virt_addr + size; offset += transport_mtu)
- memset(offset, 0, sizeof(struct ntb_payload_header));
-
/* Notify HW the memory location of the receive buffer */
ntb_set_mw_addr(nt->ndev, num_mw, mw->dma_addr);
return 0;
}
-static void ntb_qp_link_down(struct ntb_transport_qp *qp)
+static void ntb_qp_link_cleanup(struct work_struct *work)
{
+ struct ntb_transport_qp *qp = container_of(work,
+ struct ntb_transport_qp,
+ link_cleanup);
struct ntb_transport *nt = qp->transport;
struct pci_dev *pdev = ntb_query_pdev(nt->ndev);
msecs_to_jiffies(NTB_LINK_DOWN_TIMEOUT));
}
-static void ntb_transport_conn_down(struct ntb_transport *nt)
+static void ntb_qp_link_down(struct ntb_transport_qp *qp)
{
+ schedule_work(&qp->link_cleanup);
+}
+
+static void ntb_transport_link_cleanup(struct work_struct *work)
+{
+ struct ntb_transport *nt = container_of(work, struct ntb_transport,
+ link_cleanup);
int i;
if (nt->transport_link == NTB_LINK_DOWN)
schedule_delayed_work(&nt->link_work, 0);
break;
case NTB_EVENT_HW_LINK_DOWN:
- ntb_transport_conn_down(nt);
+ schedule_work(&nt->link_cleanup);
break;
default:
BUG();
unsigned int qp_num)
{
struct ntb_transport_qp *qp;
+ unsigned int num_qps_mw, tx_size;
+ u8 mw_num = QP_TO_MW(qp_num);
qp = &nt->qps[qp_num];
qp->qp_num = qp_num;
qp->client_ready = NTB_LINK_DOWN;
qp->event_handler = NULL;
+ if (nt->max_qps % NTB_NUM_MW && mw_num < nt->max_qps % NTB_NUM_MW)
+ num_qps_mw = nt->max_qps / NTB_NUM_MW + 1;
+ else
+ num_qps_mw = nt->max_qps / NTB_NUM_MW;
+
+ tx_size = (unsigned int) ntb_get_mw_size(qp->ndev, mw_num) / num_qps_mw;
+ qp->rx_info = ntb_get_mw_vbase(nt->ndev, mw_num) +
+ (qp_num / NTB_NUM_MW * tx_size);
+ tx_size -= sizeof(struct ntb_rx_info);
+
+ qp->tx_mw = qp->rx_info + sizeof(struct ntb_rx_info);
+ qp->tx_max_frame = min(transport_mtu, tx_size);
+ qp->tx_max_entry = tx_size / qp->tx_max_frame;
+ qp->tx_index = 0;
+
if (nt->debugfs_dir) {
char debugfs_name[4];
}
INIT_DELAYED_WORK(&qp->link_work, ntb_qp_link_work);
+ INIT_WORK(&qp->link_cleanup, ntb_qp_link_cleanup);
spin_lock_init(&qp->ntb_rx_pend_q_lock);
spin_lock_init(&qp->ntb_rx_free_q_lock);
ntb_transport_init_queue(nt, i);
INIT_DELAYED_WORK(&nt->link_work, ntb_transport_link_work);
+ INIT_WORK(&nt->link_cleanup, ntb_transport_link_cleanup);
rc = ntb_register_event_callback(nt->ndev,
ntb_transport_event_callback);
static void ntb_rx_copy_task(struct ntb_transport_qp *qp,
struct ntb_queue_entry *entry, void *offset)
{
-
- struct ntb_payload_header *hdr;
-
- BUG_ON(offset < qp->rx_buff_begin ||
- offset + transport_mtu >= qp->rx_buff_end);
-
- hdr = offset + transport_mtu - sizeof(struct ntb_payload_header);
- entry->len = hdr->len;
+ void *cb_data = entry->cb_data;
+ unsigned int len = entry->len;
memcpy(entry->buf, offset, entry->len);
- /* Ensure that the data is fully copied out before clearing the flag */
- wmb();
- hdr->flags = 0;
+ ntb_list_add(&qp->ntb_rx_free_q_lock, &entry->entry, &qp->rx_free_q);
if (qp->rx_handler && qp->client_ready == NTB_LINK_UP)
- qp->rx_handler(qp, qp->cb_data, entry->cb_data, entry->len);
-
- ntb_list_add(&qp->ntb_rx_free_q_lock, &entry->entry, &qp->rx_free_q);
+ qp->rx_handler(qp, qp->cb_data, cb_data, len);
}
static int ntb_process_rxc(struct ntb_transport_qp *qp)
struct ntb_queue_entry *entry;
void *offset;
+ offset = qp->rx_buff + qp->rx_max_frame * qp->rx_index;
+ hdr = offset + qp->rx_max_frame - sizeof(struct ntb_payload_header);
+
entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q);
if (!entry) {
- hdr = offset + transport_mtu -
- sizeof(struct ntb_payload_header);
dev_dbg(&ntb_query_pdev(qp->ndev)->dev,
- "no buffer - HDR ver %llu, len %d, flags %x\n",
+ "no buffer - HDR ver %u, len %d, flags %x\n",
hdr->ver, hdr->len, hdr->flags);
qp->rx_err_no_buf++;
return -ENOMEM;
}
- offset = qp->rx_offset;
- hdr = offset + transport_mtu - sizeof(struct ntb_payload_header);
-
if (!(hdr->flags & DESC_DONE_FLAG)) {
ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry,
- &qp->rx_pend_q);
+ &qp->rx_pend_q);
qp->rx_ring_empty++;
return -EAGAIN;
}
- if (hdr->ver != qp->rx_pkts) {
+ if (hdr->ver != (u32) qp->rx_pkts) {
dev_dbg(&ntb_query_pdev(qp->ndev)->dev,
- "qp %d: version mismatch, expected %llu - got %llu\n",
+ "qp %d: version mismatch, expected %llu - got %u\n",
qp->qp_num, qp->rx_pkts, hdr->ver);
ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry,
- &qp->rx_pend_q);
+ &qp->rx_pend_q);
qp->rx_err_ver++;
return -EIO;
}
ntb_qp_link_down(qp);
ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry,
- &qp->rx_pend_q);
-
- /* Ensure that the data is fully copied out before clearing the
- * done flag
- */
- wmb();
- hdr->flags = 0;
+ &qp->rx_pend_q);
goto out;
}
dev_dbg(&ntb_query_pdev(qp->ndev)->dev,
- "rx offset %p, ver %llu - %d payload received, buf size %d\n",
- qp->rx_offset, hdr->ver, hdr->len, entry->len);
+ "rx offset %u, ver %u - %d payload received, buf size %d\n",
+ qp->rx_index, hdr->ver, hdr->len, entry->len);
- if (hdr->len <= entry->len)
+ if (hdr->len <= entry->len) {
+ entry->len = hdr->len;
ntb_rx_copy_task(qp, entry, offset);
- else {
+ } else {
ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry,
- &qp->rx_pend_q);
+ &qp->rx_pend_q);
- /* Ensure that the data is fully copied out before clearing the
- * done flag
- */
- wmb();
- hdr->flags = 0;
qp->rx_err_oflow++;
dev_dbg(&ntb_query_pdev(qp->ndev)->dev,
"RX overflow! Wanted %d got %d\n",
qp->rx_pkts++;
out:
- qp->rx_offset += transport_mtu;
- if (qp->rx_offset + transport_mtu >= qp->rx_buff_end)
- qp->rx_offset = qp->rx_buff_begin;
+ /* Ensure that the data is fully copied out before clearing the flag */
+ wmb();
+ hdr->flags = 0;
+ iowrite32(qp->rx_index, &qp->rx_info->entry);
+
+ qp->rx_index++;
+ qp->rx_index %= qp->rx_max_entry;
return 0;
}
static void ntb_tx_copy_task(struct ntb_transport_qp *qp,
struct ntb_queue_entry *entry,
- void *offset)
+ void __iomem *offset)
{
- struct ntb_payload_header *hdr;
-
- BUG_ON(offset < qp->tx_mw_begin ||
- offset + transport_mtu >= qp->tx_mw_end);
+ struct ntb_payload_header __iomem *hdr;
memcpy_toio(offset, entry->buf, entry->len);
- hdr = offset + transport_mtu - sizeof(struct ntb_payload_header);
- hdr->len = entry->len;
- hdr->ver = qp->tx_pkts;
+ hdr = offset + qp->tx_max_frame - sizeof(struct ntb_payload_header);
+ iowrite32(entry->len, &hdr->len);
+ iowrite32((u32) qp->tx_pkts, &hdr->ver);
/* Ensure that the data is fully copied out before setting the flag */
- mmiowb();
- hdr->flags = entry->flags | DESC_DONE_FLAG;
+ wmb();
+ iowrite32(entry->flags | DESC_DONE_FLAG, &hdr->flags);
ntb_ring_sdb(qp->ndev, qp->qp_num);
static int ntb_process_tx(struct ntb_transport_qp *qp,
struct ntb_queue_entry *entry)
{
- struct ntb_payload_header *hdr;
- void *offset;
+ void __iomem *offset;
- offset = qp->tx_offset;
- hdr = offset + transport_mtu - sizeof(struct ntb_payload_header);
+ offset = qp->tx_mw + qp->tx_max_frame * qp->tx_index;
- dev_dbg(&ntb_query_pdev(qp->ndev)->dev, "%lld - offset %p, tx %p, entry len %d flags %x buff %p\n",
- qp->tx_pkts, offset, qp->tx_offset, entry->len, entry->flags,
- entry->buf);
- if (hdr->flags) {
+ dev_dbg(&ntb_query_pdev(qp->ndev)->dev, "%lld - offset %p, tx %u, entry len %d flags %x buff %p\n",
+ qp->tx_pkts, offset, qp->tx_index, entry->len, entry->flags,
+ entry->buf);
+ if (qp->tx_index == qp->remote_rx_info->entry) {
qp->tx_ring_full++;
return -EAGAIN;
}
- if (entry->len > transport_mtu - sizeof(struct ntb_payload_header)) {
+ if (entry->len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) {
if (qp->tx_handler)
qp->tx_handler(qp->cb_data, qp, NULL, -EIO);
ntb_tx_copy_task(qp, entry, offset);
- qp->tx_offset += transport_mtu;
- if (qp->tx_offset + transport_mtu >= qp->tx_mw_end)
- qp->tx_offset = qp->tx_mw_begin;
+ qp->tx_index++;
+ qp->tx_index %= qp->tx_max_entry;
qp->tx_pkts++;
dev_info(&pdev->dev, "qp %d: Link Down\n", qp->qp_num);
for (i = 0; i < NTB_LINK_DOWN_TIMEOUT; i++) {
- entry = ntb_list_rm(&qp->ntb_tx_free_q_lock,
- &qp->tx_free_q);
+ entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q);
if (entry)
break;
msleep(100);
goto err1;
ntb_list_add(&qp->ntb_rx_free_q_lock, &entry->entry,
- &qp->rx_free_q);
+ &qp->rx_free_q);
}
for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) {
goto err2;
ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry,
- &qp->tx_free_q);
+ &qp->tx_free_q);
}
tasklet_init(&qp->rx_work, ntb_transport_rx, (unsigned long) qp);
err3:
tasklet_disable(&qp->rx_work);
err2:
- while ((entry =
- ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
+ while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
kfree(entry);
err1:
- while ((entry =
- ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q)))
+ while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q)))
kfree(entry);
set_bit(free_queue, &nt->qp_bitmap);
err:
ntb_unregister_db_callback(qp->ndev, qp->qp_num);
tasklet_disable(&qp->rx_work);
- while ((entry =
- ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q)))
+ while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q)))
kfree(entry);
- while ((entry =
- ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q))) {
+ while ((entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q))) {
dev_warn(&pdev->dev, "Freeing item from a non-empty queue\n");
kfree(entry);
}
- while ((entry =
- ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
+ while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
kfree(entry);
set_bit(qp->qp_num, &qp->transport->qp_bitmap);
buf = entry->cb_data;
*len = entry->len;
- ntb_list_add(&qp->ntb_rx_free_q_lock, &entry->entry,
- &qp->rx_free_q);
+ ntb_list_add(&qp->ntb_rx_free_q_lock, &entry->entry, &qp->rx_free_q);
return buf;
}
entry->buf = data;
entry->len = len;
- ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry,
- &qp->rx_pend_q);
+ ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry, &qp->rx_pend_q);
return 0;
}
*
* RETURNS: the max payload size of a qp
*/
-unsigned int
-ntb_transport_max_size(__attribute__((unused)) struct ntb_transport_qp *qp)
+unsigned int ntb_transport_max_size(struct ntb_transport_qp *qp)
{
- return transport_mtu - sizeof(struct ntb_payload_header);
+ return qp->tx_max_frame - sizeof(struct ntb_payload_header);
}
EXPORT_SYMBOL_GPL(ntb_transport_max_size);