linux/drivers/ntb/test/ntb_tool.c
<<
>>
Prefs
   1/*
   2 * This file is provided under a dual BSD/GPLv2 license.  When using or
   3 *   redistributing this file, you may do so under either license.
   4 *
   5 *   GPL LICENSE SUMMARY
   6 *
   7 *   Copyright (C) 2015 EMC Corporation. All Rights Reserved.
   8 *
   9 *   This program is free software; you can redistribute it and/or modify
  10 *   it under the terms of version 2 of the GNU General Public License as
  11 *   published by the Free Software Foundation.
  12 *
  13 *   This program is distributed in the hope that it will be useful, but
  14 *   WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 *   General Public License for more details.
  17 *
  18 *   BSD LICENSE
  19 *
  20 *   Copyright (C) 2015 EMC Corporation. All Rights Reserved.
  21 *
  22 *   Redistribution and use in source and binary forms, with or without
  23 *   modification, are permitted provided that the following conditions
  24 *   are met:
  25 *
  26 *     * Redistributions of source code must retain the above copyright
  27 *       notice, this list of conditions and the following disclaimer.
  28 *     * Redistributions in binary form must reproduce the above copy
  29 *       notice, this list of conditions and the following disclaimer in
  30 *       the documentation and/or other materials provided with the
  31 *       distribution.
  32 *     * Neither the name of Intel Corporation nor the names of its
  33 *       contributors may be used to endorse or promote products derived
  34 *       from this software without specific prior written permission.
  35 *
  36 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  37 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  38 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  39 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  40 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  43 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  44 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  45 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  46 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  47 *
  48 * PCIe NTB Debugging Tool Linux driver
  49 *
  50 * Contact Information:
  51 * Allen Hubbe <Allen.Hubbe@emc.com>
  52 */
  53
  54/*
  55 * How to use this tool, by example.
  56 *
  57 * Assuming $DBG_DIR is something like:
  58 * '/sys/kernel/debug/ntb_tool/0000:00:03.0'
  59 *
  60 * Eg: check if clearing the doorbell mask generates an interrupt.
  61 *
  62 * # Set the doorbell mask
  63 * root@self# echo 's 1' > $DBG_DIR/mask
  64 *
  65 * # Ring the doorbell from the peer
  66 * root@peer# echo 's 1' > $DBG_DIR/peer_db
  67 *
  68 * # Clear the doorbell mask
  69 * root@self# echo 'c 1' > $DBG_DIR/mask
  70 *
  71 * Observe debugging output in dmesg or your console.  You should see a
  72 * doorbell event triggered by clearing the mask.  If not, this may indicate an
  73 * issue with the hardware that needs to be worked around in the driver.
  74 *
  75 * Eg: read and write scratchpad registers
  76 *
  77 * root@peer# echo '0 0x01010101 1 0x7f7f7f7f' > $DBG_DIR/peer_spad
  78 *
  79 * root@self# cat $DBG_DIR/spad
  80 *
  81 * Observe that spad 0 and 1 have the values set by the peer.
  82 */
  83
  84#include <linux/init.h>
  85#include <linux/kernel.h>
  86#include <linux/module.h>
  87
  88#include <linux/debugfs.h>
  89#include <linux/dma-mapping.h>
  90#include <linux/pci.h>
  91#include <linux/slab.h>
  92
  93#include <linux/ntb.h>
  94
  95#define DRIVER_NAME                     "ntb_tool"
  96#define DRIVER_DESCRIPTION              "PCIe NTB Debugging Tool"
  97
  98#define DRIVER_LICENSE                  "Dual BSD/GPL"
  99#define DRIVER_VERSION                  "1.0"
 100#define DRIVER_RELDATE                  "22 April 2015"
 101#define DRIVER_AUTHOR                   "Allen Hubbe <Allen.Hubbe@emc.com>"
 102
 103MODULE_LICENSE(DRIVER_LICENSE);
 104MODULE_VERSION(DRIVER_VERSION);
 105MODULE_AUTHOR(DRIVER_AUTHOR);
 106MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
 107
 108static struct dentry *tool_dbgfs;
 109
 110struct tool_ctx {
 111        struct ntb_dev *ntb;
 112        struct dentry *dbgfs;
 113};
 114
 115#define SPAD_FNAME_SIZE 0x10
 116#define INT_PTR(x) ((void *)(unsigned long)x)
 117#define PTR_INT(x) ((int)(unsigned long)x)
 118
 119#define TOOL_FOPS_RDWR(__name, __read, __write) \
 120        const struct file_operations __name = { \
 121                .owner = THIS_MODULE,           \
 122                .open = simple_open,            \
 123                .read = __read,                 \
 124                .write = __write,               \
 125        }
 126
 127static void tool_link_event(void *ctx)
 128{
 129        struct tool_ctx *tc = ctx;
 130        enum ntb_speed speed;
 131        enum ntb_width width;
 132        int up;
 133
 134        up = ntb_link_is_up(tc->ntb, &speed, &width);
 135
 136        dev_dbg(&tc->ntb->dev, "link is %s speed %d width %d\n",
 137                up ? "up" : "down", speed, width);
 138}
 139
 140static void tool_db_event(void *ctx, int vec)
 141{
 142        struct tool_ctx *tc = ctx;
 143        u64 db_bits, db_mask;
 144
 145        db_mask = ntb_db_vector_mask(tc->ntb, vec);
 146        db_bits = ntb_db_read(tc->ntb);
 147
 148        dev_dbg(&tc->ntb->dev, "doorbell vec %d mask %#llx bits %#llx\n",
 149                vec, db_mask, db_bits);
 150}
 151
 152static const struct ntb_ctx_ops tool_ops = {
 153        .link_event = tool_link_event,
 154        .db_event = tool_db_event,
 155};
 156
 157static ssize_t tool_dbfn_read(struct tool_ctx *tc, char __user *ubuf,
 158                              size_t size, loff_t *offp,
 159                              u64 (*db_read_fn)(struct ntb_dev *))
 160{
 161        size_t buf_size;
 162        char *buf;
 163        ssize_t pos, rc;
 164
 165        if (!db_read_fn)
 166                return -EINVAL;
 167
 168        buf_size = min_t(size_t, size, 0x20);
 169
 170        buf = kmalloc(buf_size, GFP_KERNEL);
 171        if (!buf)
 172                return -ENOMEM;
 173
 174        pos = scnprintf(buf, buf_size, "%#llx\n",
 175                        db_read_fn(tc->ntb));
 176
 177        rc = simple_read_from_buffer(ubuf, size, offp, buf, pos);
 178
 179        kfree(buf);
 180
 181        return rc;
 182}
 183
 184static ssize_t tool_dbfn_write(struct tool_ctx *tc,
 185                               const char __user *ubuf,
 186                               size_t size, loff_t *offp,
 187                               int (*db_set_fn)(struct ntb_dev *, u64),
 188                               int (*db_clear_fn)(struct ntb_dev *, u64))
 189{
 190        u64 db_bits;
 191        char *buf, cmd;
 192        ssize_t rc;
 193        int n;
 194
 195        buf = kmalloc(size + 1, GFP_KERNEL);
 196        if (!buf)
 197                return -ENOMEM;
 198
 199        rc = simple_write_to_buffer(buf, size, offp, ubuf, size);
 200        if (rc < 0) {
 201                kfree(buf);
 202                return rc;
 203        }
 204
 205        buf[size] = 0;
 206
 207        n = sscanf(buf, "%c %lli", &cmd, &db_bits);
 208
 209        kfree(buf);
 210
 211        if (n != 2) {
 212                rc = -EINVAL;
 213        } else if (cmd == 's') {
 214                if (!db_set_fn)
 215                        rc = -EINVAL;
 216                else
 217                        rc = db_set_fn(tc->ntb, db_bits);
 218        } else if (cmd == 'c') {
 219                if (!db_clear_fn)
 220                        rc = -EINVAL;
 221                else
 222                        rc = db_clear_fn(tc->ntb, db_bits);
 223        } else {
 224                rc = -EINVAL;
 225        }
 226
 227        return rc ? : size;
 228}
 229
 230static ssize_t tool_spadfn_read(struct tool_ctx *tc, char __user *ubuf,
 231                                size_t size, loff_t *offp,
 232                                u32 (*spad_read_fn)(struct ntb_dev *, int))
 233{
 234        size_t buf_size;
 235        char *buf;
 236        ssize_t pos, rc;
 237        int i, spad_count;
 238
 239        if (!spad_read_fn)
 240                return -EINVAL;
 241
 242        buf_size = min_t(size_t, size, 0x100);
 243
 244        buf = kmalloc(buf_size, GFP_KERNEL);
 245        if (!buf)
 246                return -ENOMEM;
 247
 248        pos = 0;
 249
 250        spad_count = ntb_spad_count(tc->ntb);
 251        for (i = 0; i < spad_count; ++i) {
 252                pos += scnprintf(buf + pos, buf_size - pos, "%d\t%#x\n",
 253                                 i, spad_read_fn(tc->ntb, i));
 254        }
 255
 256        rc = simple_read_from_buffer(ubuf, size, offp, buf, pos);
 257
 258        kfree(buf);
 259
 260        return rc;
 261}
 262
 263static ssize_t tool_spadfn_write(struct tool_ctx *tc,
 264                                 const char __user *ubuf,
 265                                 size_t size, loff_t *offp,
 266                                 int (*spad_write_fn)(struct ntb_dev *,
 267                                                      int, u32))
 268{
 269        int spad_idx;
 270        u32 spad_val;
 271        char *buf;
 272        int pos, n;
 273        ssize_t rc;
 274
 275        if (!spad_write_fn) {
 276                dev_dbg(&tc->ntb->dev, "no spad write fn\n");
 277                return -EINVAL;
 278        }
 279
 280        buf = kmalloc(size + 1, GFP_KERNEL);
 281        if (!buf)
 282                return -ENOMEM;
 283
 284        rc = simple_write_to_buffer(buf, size, offp, ubuf, size);
 285        if (rc < 0) {
 286                kfree(buf);
 287                return rc;
 288        }
 289
 290        buf[size] = 0;
 291
 292        n = sscanf(buf, "%d %i%n", &spad_idx, &spad_val, &pos);
 293        while (n == 2) {
 294                rc = spad_write_fn(tc->ntb, spad_idx, spad_val);
 295                if (rc)
 296                        break;
 297
 298                n = sscanf(buf + pos, "%d %i%n", &spad_idx, &spad_val, &pos);
 299        }
 300
 301        if (n < 0)
 302                rc = n;
 303
 304        kfree(buf);
 305
 306        return rc ? : size;
 307}
 308
 309static ssize_t tool_db_read(struct file *filep, char __user *ubuf,
 310                            size_t size, loff_t *offp)
 311{
 312        struct tool_ctx *tc = filep->private_data;
 313
 314        return tool_dbfn_read(tc, ubuf, size, offp,
 315                              tc->ntb->ops->db_read);
 316}
 317
 318static ssize_t tool_db_write(struct file *filep, const char __user *ubuf,
 319                             size_t size, loff_t *offp)
 320{
 321        struct tool_ctx *tc = filep->private_data;
 322
 323        return tool_dbfn_write(tc, ubuf, size, offp,
 324                               tc->ntb->ops->db_set,
 325                               tc->ntb->ops->db_clear);
 326}
 327
 328static TOOL_FOPS_RDWR(tool_db_fops,
 329                      tool_db_read,
 330                      tool_db_write);
 331
 332static ssize_t tool_mask_read(struct file *filep, char __user *ubuf,
 333                              size_t size, loff_t *offp)
 334{
 335        struct tool_ctx *tc = filep->private_data;
 336
 337        return tool_dbfn_read(tc, ubuf, size, offp,
 338                              tc->ntb->ops->db_read_mask);
 339}
 340
 341static ssize_t tool_mask_write(struct file *filep, const char __user *ubuf,
 342                               size_t size, loff_t *offp)
 343{
 344        struct tool_ctx *tc = filep->private_data;
 345
 346        return tool_dbfn_write(tc, ubuf, size, offp,
 347                               tc->ntb->ops->db_set_mask,
 348                               tc->ntb->ops->db_clear_mask);
 349}
 350
 351static TOOL_FOPS_RDWR(tool_mask_fops,
 352                      tool_mask_read,
 353                      tool_mask_write);
 354
 355static ssize_t tool_peer_db_read(struct file *filep, char __user *ubuf,
 356                                 size_t size, loff_t *offp)
 357{
 358        struct tool_ctx *tc = filep->private_data;
 359
 360        return tool_dbfn_read(tc, ubuf, size, offp,
 361                              tc->ntb->ops->peer_db_read);
 362}
 363
 364static ssize_t tool_peer_db_write(struct file *filep, const char __user *ubuf,
 365                                  size_t size, loff_t *offp)
 366{
 367        struct tool_ctx *tc = filep->private_data;
 368
 369        return tool_dbfn_write(tc, ubuf, size, offp,
 370                               tc->ntb->ops->peer_db_set,
 371                               tc->ntb->ops->peer_db_clear);
 372}
 373
 374static TOOL_FOPS_RDWR(tool_peer_db_fops,
 375                      tool_peer_db_read,
 376                      tool_peer_db_write);
 377
 378static ssize_t tool_peer_mask_read(struct file *filep, char __user *ubuf,
 379                                   size_t size, loff_t *offp)
 380{
 381        struct tool_ctx *tc = filep->private_data;
 382
 383        return tool_dbfn_read(tc, ubuf, size, offp,
 384                              tc->ntb->ops->peer_db_read_mask);
 385}
 386
 387static ssize_t tool_peer_mask_write(struct file *filep, const char __user *ubuf,
 388                                    size_t size, loff_t *offp)
 389{
 390        struct tool_ctx *tc = filep->private_data;
 391
 392        return tool_dbfn_write(tc, ubuf, size, offp,
 393                               tc->ntb->ops->peer_db_set_mask,
 394                               tc->ntb->ops->peer_db_clear_mask);
 395}
 396
 397static TOOL_FOPS_RDWR(tool_peer_mask_fops,
 398                      tool_peer_mask_read,
 399                      tool_peer_mask_write);
 400
 401static ssize_t tool_spad_read(struct file *filep, char __user *ubuf,
 402                              size_t size, loff_t *offp)
 403{
 404        struct tool_ctx *tc = filep->private_data;
 405
 406        return tool_spadfn_read(tc, ubuf, size, offp,
 407                                tc->ntb->ops->spad_read);
 408}
 409
 410static ssize_t tool_spad_write(struct file *filep, const char __user *ubuf,
 411                               size_t size, loff_t *offp)
 412{
 413        struct tool_ctx *tc = filep->private_data;
 414
 415        return tool_spadfn_write(tc, ubuf, size, offp,
 416                                 tc->ntb->ops->spad_write);
 417}
 418
 419static TOOL_FOPS_RDWR(tool_spad_fops,
 420                      tool_spad_read,
 421                      tool_spad_write);
 422
 423static ssize_t tool_peer_spad_read(struct file *filep, char __user *ubuf,
 424                                   size_t size, loff_t *offp)
 425{
 426        struct tool_ctx *tc = filep->private_data;
 427
 428        return tool_spadfn_read(tc, ubuf, size, offp,
 429                                tc->ntb->ops->peer_spad_read);
 430}
 431
 432static ssize_t tool_peer_spad_write(struct file *filep, const char __user *ubuf,
 433                                    size_t size, loff_t *offp)
 434{
 435        struct tool_ctx *tc = filep->private_data;
 436
 437        return tool_spadfn_write(tc, ubuf, size, offp,
 438                                 tc->ntb->ops->peer_spad_write);
 439}
 440
 441static TOOL_FOPS_RDWR(tool_peer_spad_fops,
 442                      tool_peer_spad_read,
 443                      tool_peer_spad_write);
 444
 445static void tool_setup_dbgfs(struct tool_ctx *tc)
 446{
 447        /* This modules is useless without dbgfs... */
 448        if (!tool_dbgfs) {
 449                tc->dbgfs = NULL;
 450                return;
 451        }
 452
 453        tc->dbgfs = debugfs_create_dir(dev_name(&tc->ntb->dev),
 454                                       tool_dbgfs);
 455        if (!tc->dbgfs)
 456                return;
 457
 458        debugfs_create_file("db", S_IRUSR | S_IWUSR, tc->dbgfs,
 459                            tc, &tool_db_fops);
 460
 461        debugfs_create_file("mask", S_IRUSR | S_IWUSR, tc->dbgfs,
 462                            tc, &tool_mask_fops);
 463
 464        debugfs_create_file("peer_db", S_IRUSR | S_IWUSR, tc->dbgfs,
 465                            tc, &tool_peer_db_fops);
 466
 467        debugfs_create_file("peer_mask", S_IRUSR | S_IWUSR, tc->dbgfs,
 468                            tc, &tool_peer_mask_fops);
 469
 470        debugfs_create_file("spad", S_IRUSR | S_IWUSR, tc->dbgfs,
 471                            tc, &tool_spad_fops);
 472
 473        debugfs_create_file("peer_spad", S_IRUSR | S_IWUSR, tc->dbgfs,
 474                            tc, &tool_peer_spad_fops);
 475}
 476
 477static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb)
 478{
 479        struct tool_ctx *tc;
 480        int rc;
 481
 482        if (ntb_db_is_unsafe(ntb))
 483                dev_dbg(&ntb->dev, "doorbell is unsafe\n");
 484
 485        if (ntb_spad_is_unsafe(ntb))
 486                dev_dbg(&ntb->dev, "scratchpad is unsafe\n");
 487
 488        tc = kmalloc(sizeof(*tc), GFP_KERNEL);
 489        if (!tc) {
 490                rc = -ENOMEM;
 491                goto err_tc;
 492        }
 493
 494        tc->ntb = ntb;
 495
 496        tool_setup_dbgfs(tc);
 497
 498        rc = ntb_set_ctx(ntb, tc, &tool_ops);
 499        if (rc)
 500                goto err_ctx;
 501
 502        ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
 503        ntb_link_event(ntb);
 504
 505        return 0;
 506
 507err_ctx:
 508        debugfs_remove_recursive(tc->dbgfs);
 509        kfree(tc);
 510err_tc:
 511        return rc;
 512}
 513
 514static void tool_remove(struct ntb_client *self, struct ntb_dev *ntb)
 515{
 516        struct tool_ctx *tc = ntb->ctx;
 517
 518        ntb_clear_ctx(ntb);
 519        ntb_link_disable(ntb);
 520
 521        debugfs_remove_recursive(tc->dbgfs);
 522        kfree(tc);
 523}
 524
 525static struct ntb_client tool_client = {
 526        .ops = {
 527                .probe = tool_probe,
 528                .remove = tool_remove,
 529        },
 530};
 531
 532static int __init tool_init(void)
 533{
 534        int rc;
 535
 536        if (debugfs_initialized())
 537                tool_dbgfs = debugfs_create_dir(KBUILD_MODNAME, NULL);
 538
 539        rc = ntb_register_client(&tool_client);
 540        if (rc)
 541                goto err_client;
 542
 543        return 0;
 544
 545err_client:
 546        debugfs_remove_recursive(tool_dbgfs);
 547        return rc;
 548}
 549module_init(tool_init);
 550
 551static void __exit tool_exit(void)
 552{
 553        ntb_unregister_client(&tool_client);
 554        debugfs_remove_recursive(tool_dbgfs);
 555}
 556module_exit(tool_exit);
 557