#define pr_fmt(fmt) "drbd debugfs: " fmt #include #include #include #include #include #include "drbd_int.h" #include "drbd_req.h" #include "drbd_debugfs.h" static struct dentry *drbd_debugfs_root; static struct dentry *drbd_debugfs_resources; static struct dentry *drbd_debugfs_minors; void drbd_debugfs_resource_add(struct drbd_resource *resource) { struct dentry *dentry; if (!drbd_debugfs_resources) return; dentry = debugfs_create_dir(resource->name, drbd_debugfs_resources); if (IS_ERR_OR_NULL(dentry)) goto fail; resource->debugfs_res = dentry; dentry = debugfs_create_dir("volumes", resource->debugfs_res); if (IS_ERR_OR_NULL(dentry)) goto fail; resource->debugfs_res_volumes = dentry; dentry = debugfs_create_dir("connections", resource->debugfs_res); if (IS_ERR_OR_NULL(dentry)) goto fail; resource->debugfs_res_connections = dentry; return; fail: drbd_debugfs_resource_cleanup(resource); drbd_err(resource, "failed to create debugfs dentry\n"); } static void drbd_debugfs_remove(struct dentry **dp) { debugfs_remove(*dp); *dp = NULL; } void drbd_debugfs_resource_cleanup(struct drbd_resource *resource) { /* it is ok to call debugfs_remove(NULL) */ drbd_debugfs_remove(&resource->debugfs_res_in_flight_summary); drbd_debugfs_remove(&resource->debugfs_res_connections); drbd_debugfs_remove(&resource->debugfs_res_volumes); drbd_debugfs_remove(&resource->debugfs_res); } void drbd_debugfs_connection_add(struct drbd_connection *connection) { struct dentry *conns_dir = connection->resource->debugfs_res_connections; struct dentry *dentry; if (!conns_dir) return; /* Once we enable mutliple peers, * these connections will have descriptive names. * For now, it is just the one connection to the (only) "peer". */ dentry = debugfs_create_dir("peer", conns_dir); if (IS_ERR_OR_NULL(dentry)) goto fail; connection->debugfs_conn = dentry; return; fail: drbd_debugfs_connection_cleanup(connection); drbd_err(connection, "failed to create debugfs dentry\n"); } void drbd_debugfs_connection_cleanup(struct drbd_connection *connection) { drbd_debugfs_remove(&connection->debugfs_conn_callback_history); drbd_debugfs_remove(&connection->debugfs_conn_oldest_requests); drbd_debugfs_remove(&connection->debugfs_conn); } void drbd_debugfs_device_add(struct drbd_device *device) { struct dentry *vols_dir = device->resource->debugfs_res_volumes; char minor_buf[8]; /* MINORMASK, MINORBITS == 20; */ char vnr_buf[8]; /* volume number vnr is even 16 bit only; */ char *slink_name = NULL; struct dentry *dentry; if (!vols_dir || !drbd_debugfs_minors) return; snprintf(vnr_buf, sizeof(vnr_buf), "%u", device->vnr); dentry = debugfs_create_dir(vnr_buf, vols_dir); if (IS_ERR_OR_NULL(dentry)) goto fail; device->debugfs_vol = dentry; snprintf(minor_buf, sizeof(minor_buf), "%u", device->minor); slink_name = kasprintf(GFP_KERNEL, "../resources/%s/volumes/%u", device->resource->name, device->vnr); if (!slink_name) goto fail; dentry = debugfs_create_symlink(minor_buf, drbd_debugfs_minors, slink_name); if (IS_ERR_OR_NULL(dentry)) goto fail; device->debugfs_minor = dentry; kfree(slink_name); fail: drbd_debugfs_device_cleanup(device); drbd_err(device, "failed to create debugfs entries\n"); } void drbd_debugfs_device_cleanup(struct drbd_device *device) { drbd_debugfs_remove(&device->debugfs_minor); drbd_debugfs_remove(&device->debugfs_vol_oldest_requests); drbd_debugfs_remove(&device->debugfs_vol_act_log_extents); drbd_debugfs_remove(&device->debugfs_vol_resync_extents); drbd_debugfs_remove(&device->debugfs_vol_data_gen_id); drbd_debugfs_remove(&device->debugfs_vol); } void drbd_debugfs_peer_device_add(struct drbd_peer_device *peer_device) { struct dentry *conn_dir = peer_device->connection->debugfs_conn; struct dentry *dentry; char vnr_buf[8]; if (!conn_dir) return; snprintf(vnr_buf, sizeof(vnr_buf), "%u", peer_device->device->vnr); dentry = debugfs_create_dir(vnr_buf, conn_dir); if (IS_ERR_OR_NULL(dentry)) goto fail; peer_device->debugfs_peer_dev = dentry; return; fail: drbd_debugfs_peer_device_cleanup(peer_device); drbd_err(peer_device, "failed to create debugfs entries\n"); } void drbd_debugfs_peer_device_cleanup(struct drbd_peer_device *peer_device) { drbd_debugfs_remove(&peer_device->debugfs_peer_dev); } /* not __exit, may be indirectly called * from the module-load-failure path as well. */ void drbd_debugfs_cleanup(void) { drbd_debugfs_remove(&drbd_debugfs_resources); drbd_debugfs_remove(&drbd_debugfs_minors); drbd_debugfs_remove(&drbd_debugfs_root); } int __init drbd_debugfs_init(void) { struct dentry *dentry; dentry = debugfs_create_dir("drbd", NULL); if (IS_ERR_OR_NULL(dentry)) goto fail; drbd_debugfs_root = dentry; dentry = debugfs_create_dir("resources", drbd_debugfs_root); if (IS_ERR_OR_NULL(dentry)) goto fail; drbd_debugfs_resources = dentry; dentry = debugfs_create_dir("minors", drbd_debugfs_root); if (IS_ERR_OR_NULL(dentry)) goto fail; drbd_debugfs_minors = dentry; return 0; fail: drbd_debugfs_cleanup(); if (dentry) return PTR_ERR(dentry); else return -EINVAL; }