| /* | |
| * Intel MIC Platform Software Stack (MPSS) | |
| * | |
| * Copyright(c) 2016 Intel Corporation. | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License, version 2, as | |
| * published by the Free Software Foundation. | |
| * | |
| * 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. | |
| * | |
| * The full GNU General Public License is included in this distribution in | |
| * the file called "COPYING". | |
| * | |
| * Intel Virtio Over PCIe (VOP) driver. | |
| * | |
| */ | |
| #include <linux/debugfs.h> | |
| #include <linux/seq_file.h> | |
| #include "vop_main.h" | |
| static int vop_dp_show(struct seq_file *s, void *pos) | |
| { | |
| struct mic_device_desc *d; | |
| struct mic_device_ctrl *dc; | |
| struct mic_vqconfig *vqconfig; | |
| __u32 *features; | |
| __u8 *config; | |
| struct vop_info *vi = s->private; | |
| struct vop_device *vpdev = vi->vpdev; | |
| struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); | |
| int j, k; | |
| seq_printf(s, "Bootparam: magic 0x%x\n", | |
| bootparam->magic); | |
| seq_printf(s, "Bootparam: h2c_config_db %d\n", | |
| bootparam->h2c_config_db); | |
| seq_printf(s, "Bootparam: node_id %d\n", | |
| bootparam->node_id); | |
| seq_printf(s, "Bootparam: c2h_scif_db %d\n", | |
| bootparam->c2h_scif_db); | |
| seq_printf(s, "Bootparam: h2c_scif_db %d\n", | |
| bootparam->h2c_scif_db); | |
| seq_printf(s, "Bootparam: scif_host_dma_addr 0x%llx\n", | |
| bootparam->scif_host_dma_addr); | |
| seq_printf(s, "Bootparam: scif_card_dma_addr 0x%llx\n", | |
| bootparam->scif_card_dma_addr); | |
| for (j = sizeof(*bootparam); | |
| j < MIC_DP_SIZE; j += mic_total_desc_size(d)) { | |
| d = (void *)bootparam + j; | |
| dc = (void *)d + mic_aligned_desc_size(d); | |
| /* end of list */ | |
| if (d->type == 0) | |
| break; | |
| if (d->type == -1) | |
| continue; | |
| seq_printf(s, "Type %d ", d->type); | |
| seq_printf(s, "Num VQ %d ", d->num_vq); | |
| seq_printf(s, "Feature Len %d\n", d->feature_len); | |
| seq_printf(s, "Config Len %d ", d->config_len); | |
| seq_printf(s, "Shutdown Status %d\n", d->status); | |
| for (k = 0; k < d->num_vq; k++) { | |
| vqconfig = mic_vq_config(d) + k; | |
| seq_printf(s, "vqconfig[%d]: ", k); | |
| seq_printf(s, "address 0x%llx ", | |
| vqconfig->address); | |
| seq_printf(s, "num %d ", vqconfig->num); | |
| seq_printf(s, "used address 0x%llx\n", | |
| vqconfig->used_address); | |
| } | |
| features = (__u32 *)mic_vq_features(d); | |
| seq_printf(s, "Features: Host 0x%x ", features[0]); | |
| seq_printf(s, "Guest 0x%x\n", features[1]); | |
| config = mic_vq_configspace(d); | |
| for (k = 0; k < d->config_len; k++) | |
| seq_printf(s, "config[%d]=%d\n", k, config[k]); | |
| seq_puts(s, "Device control:\n"); | |
| seq_printf(s, "Config Change %d ", dc->config_change); | |
| seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); | |
| seq_printf(s, "Guest Ack %d ", dc->guest_ack); | |
| seq_printf(s, "Host ack %d\n", dc->host_ack); | |
| seq_printf(s, "Used address updated %d ", | |
| dc->used_address_updated); | |
| seq_printf(s, "Vdev 0x%llx\n", dc->vdev); | |
| seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); | |
| seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); | |
| } | |
| schedule_work(&vi->hotplug_work); | |
| return 0; | |
| } | |
| static int vop_dp_debug_open(struct inode *inode, struct file *file) | |
| { | |
| return single_open(file, vop_dp_show, inode->i_private); | |
| } | |
| static int vop_dp_debug_release(struct inode *inode, struct file *file) | |
| { | |
| return single_release(inode, file); | |
| } | |
| static const struct file_operations dp_ops = { | |
| .owner = THIS_MODULE, | |
| .open = vop_dp_debug_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = vop_dp_debug_release | |
| }; | |
| static int vop_vdev_info_show(struct seq_file *s, void *unused) | |
| { | |
| struct vop_info *vi = s->private; | |
| struct list_head *pos, *tmp; | |
| struct vop_vdev *vdev; | |
| int i, j; | |
| mutex_lock(&vi->vop_mutex); | |
| list_for_each_safe(pos, tmp, &vi->vdev_list) { | |
| vdev = list_entry(pos, struct vop_vdev, list); | |
| seq_printf(s, "VDEV type %d state %s in %ld out %ld in_dma %ld out_dma %ld\n", | |
| vdev->virtio_id, | |
| vop_vdevup(vdev) ? "UP" : "DOWN", | |
| vdev->in_bytes, | |
| vdev->out_bytes, | |
| vdev->in_bytes_dma, | |
| vdev->out_bytes_dma); | |
| for (i = 0; i < MIC_MAX_VRINGS; i++) { | |
| struct vring_desc *desc; | |
| struct vring_avail *avail; | |
| struct vring_used *used; | |
| struct vop_vringh *vvr = &vdev->vvr[i]; | |
| struct vringh *vrh = &vvr->vrh; | |
| int num = vrh->vring.num; | |
| if (!num) | |
| continue; | |
| desc = vrh->vring.desc; | |
| seq_printf(s, "vring i %d avail_idx %d", | |
| i, vvr->vring.info->avail_idx & (num - 1)); | |
| seq_printf(s, " vring i %d avail_idx %d\n", | |
| i, vvr->vring.info->avail_idx); | |
| seq_printf(s, "vrh i %d weak_barriers %d", | |
| i, vrh->weak_barriers); | |
| seq_printf(s, " last_avail_idx %d last_used_idx %d", | |
| vrh->last_avail_idx, vrh->last_used_idx); | |
| seq_printf(s, " completed %d\n", vrh->completed); | |
| for (j = 0; j < num; j++) { | |
| seq_printf(s, "desc[%d] addr 0x%llx len %d", | |
| j, desc->addr, desc->len); | |
| seq_printf(s, " flags 0x%x next %d\n", | |
| desc->flags, desc->next); | |
| desc++; | |
| } | |
| avail = vrh->vring.avail; | |
| seq_printf(s, "avail flags 0x%x idx %d\n", | |
| vringh16_to_cpu(vrh, avail->flags), | |
| vringh16_to_cpu(vrh, | |
| avail->idx) & (num - 1)); | |
| seq_printf(s, "avail flags 0x%x idx %d\n", | |
| vringh16_to_cpu(vrh, avail->flags), | |
| vringh16_to_cpu(vrh, avail->idx)); | |
| for (j = 0; j < num; j++) | |
| seq_printf(s, "avail ring[%d] %d\n", | |
| j, avail->ring[j]); | |
| used = vrh->vring.used; | |
| seq_printf(s, "used flags 0x%x idx %d\n", | |
| vringh16_to_cpu(vrh, used->flags), | |
| vringh16_to_cpu(vrh, used->idx) & (num - 1)); | |
| seq_printf(s, "used flags 0x%x idx %d\n", | |
| vringh16_to_cpu(vrh, used->flags), | |
| vringh16_to_cpu(vrh, used->idx)); | |
| for (j = 0; j < num; j++) | |
| seq_printf(s, "used ring[%d] id %d len %d\n", | |
| j, vringh32_to_cpu(vrh, | |
| used->ring[j].id), | |
| vringh32_to_cpu(vrh, | |
| used->ring[j].len)); | |
| } | |
| } | |
| mutex_unlock(&vi->vop_mutex); | |
| return 0; | |
| } | |
| static int vop_vdev_info_debug_open(struct inode *inode, struct file *file) | |
| { | |
| return single_open(file, vop_vdev_info_show, inode->i_private); | |
| } | |
| static int vop_vdev_info_debug_release(struct inode *inode, struct file *file) | |
| { | |
| return single_release(inode, file); | |
| } | |
| static const struct file_operations vdev_info_ops = { | |
| .owner = THIS_MODULE, | |
| .open = vop_vdev_info_debug_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = vop_vdev_info_debug_release | |
| }; | |
| void vop_init_debugfs(struct vop_info *vi) | |
| { | |
| char name[16]; | |
| snprintf(name, sizeof(name), "%s%d", KBUILD_MODNAME, vi->vpdev->dnode); | |
| vi->dbg = debugfs_create_dir(name, NULL); | |
| if (!vi->dbg) { | |
| pr_err("can't create debugfs dir vop\n"); | |
| return; | |
| } | |
| debugfs_create_file("dp", 0444, vi->dbg, vi, &dp_ops); | |
| debugfs_create_file("vdev_info", 0444, vi->dbg, vi, &vdev_info_ops); | |
| } | |
| void vop_exit_debugfs(struct vop_info *vi) | |
| { | |
| debugfs_remove_recursive(vi->dbg); | |
| } |