#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
+#include <time.h>
#include <ccan/array_size/array_size.h>
#include <ccan/endian/endian.h>
ep->quirks = 0;
}
+static const int nsec_per_sec = 1000 * 1000 * 1000;
+/* timercmp and timersub, but for struct timespec */
+#define timespec_cmp(a, b, CMP) \
+ (((a)->tv_sec == (b)->tv_sec) \
+ ? ((a)->tv_nsec CMP (b)->tv_nsec) \
+ : ((a)->tv_sec CMP (b)->tv_sec))
+
+#define timespec_sub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \
+ if ((result)->tv_nsec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_nsec += nsec_per_sec; \
+ } \
+ } while (0)
+
+static void nvme_mi_insert_delay(struct nvme_mi_ep *ep)
+{
+ struct timespec now, next, delay;
+ int rc;
+
+ if (!ep->last_resp_time_valid)
+ return;
+
+ /* calculate earliest next command time */
+ next.tv_nsec = ep->last_resp_time.tv_nsec + ep->inter_command_us * 1000;
+ next.tv_sec = ep->last_resp_time.tv_sec;
+ if (next.tv_nsec > nsec_per_sec) {
+ next.tv_nsec -= nsec_per_sec;
+ next.tv_sec += 1;
+ }
+
+ rc = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (rc) {
+ /* not much we can do; continue immediately */
+ return;
+ }
+
+ if (timespec_cmp(&now, &next, >=))
+ return;
+
+ timespec_sub(&next, &now, &delay);
+
+ nanosleep(&delay, NULL);
+}
+
+static void nvme_mi_record_resp_time(struct nvme_mi_ep *ep)
+{
+ int rc;
+
+ rc = clock_gettime(CLOCK_MONOTONIC, &ep->last_resp_time);
+ ep->last_resp_time_valid = !rc;
+}
+
+
struct nvme_mi_ep *nvme_mi_init_ep(nvme_root_t root)
{
struct nvme_mi_ep *ep;
if (ep->transport->mic_enabled)
nvme_mi_calc_req_mic(req);
+ if (nvme_mi_ep_has_quirk(ep, NVME_QUIRK_MIN_INTER_COMMAND_TIME))
+ nvme_mi_insert_delay(ep);
+
rc = ep->transport->submit(ep, req, resp);
+
+ if (nvme_mi_ep_has_quirk(ep, NVME_QUIRK_MIN_INTER_COMMAND_TIME))
+ nvme_mi_record_resp_time(ep);
+
if (rc) {
nvme_msg(ep->root, LOG_INFO, "transport failure\n");
return rc;
int (*check_timeout)(struct nvme_mi_ep *ep, unsigned int timeout);
};
+/* quirks */
+
+/* Set a minimum time between receiving a response from one command and
+ * sending the next request. Some devices may ignore new commands sent too soon
+ * after the previous request, so manually insert a delay
+ */
+#define NVME_QUIRK_MIN_INTER_COMMAND_TIME (1 << 0)
+
struct nvme_mi_ep {
struct nvme_root *root;
const struct nvme_mi_transport *transport;
unsigned int timeout;
unsigned int mprt_max;
unsigned long quirks;
+
+ /* inter-command delay, for NVME_QUIRK_MIN_INTER_COMMAND_TIME */
+ unsigned int inter_command_us;
+ struct timespec last_resp_time;
+ bool last_resp_time_valid;
};
struct nvme_mi_ctrl {