Similarly to the sh plugin, we want to let nbdkit advertise fast zero support when it will not be calling into our .zero. And just like the sh plugin, this requires that we cache a bit of information ourselves (since it would be a layering violation to get what nbdkit has itself cached). Thankfully, our defaults for .can_fua are easier implement correctly than what the sh plugin has to do. To demonstrate this, try adding: def can_zero(h): print("can_zero") return False to example.py, before running ./nbdkit python plugins/python/example.py --run 'qemu-nbd --list' Signed-off-by: Eric Blake <eblake@redhat.com> --- An audit of the other language plugins did not find any more missing caching at this point. plugins/python/python.c | 103 ++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/plugins/python/python.c b/plugins/python/python.c index 72f37dd7..a1a0438b 100644 --- a/plugins/python/python.c +++ b/plugins/python/python.c @@ -1,5 +1,5 @@ /* nbdkit - * Copyright (C) 2013-2018 Red Hat Inc. + * Copyright (C) 2013-2020 Red Hat Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -455,51 +455,67 @@ py_get_ready (void) return 0; } +struct handle { + int can_zero; + PyObject *py_h; +}; + static void * py_open (int readonly) { PyObject *fn; - PyObject *handle; + struct handle *h; if (!callback_defined ("open", &fn)) { nbdkit_error ("%s: missing callback: %s", script, "open"); return NULL; } + h = malloc (sizeof *h); + if (h == NULL) { + nbdkit_error ("malloc: %m"); + return NULL; + } + h->can_zero = -1; + PyErr_Clear (); - handle = PyObject_CallFunctionObjArgs (fn, readonly ? Py_True : Py_False, - NULL); + h->py_h = PyObject_CallFunctionObjArgs (fn, readonly ? Py_True : Py_False, + NULL); Py_DECREF (fn); - if (check_python_failure ("open") == -1) + if (check_python_failure ("open") == -1) { + free (h); return NULL; + } - return handle; + assert (h->py_h); + return h; } static void py_close (void *handle) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; if (callback_defined ("close", &fn)) { PyErr_Clear (); - r = PyObject_CallFunctionObjArgs (fn, obj, NULL); + r = PyObject_CallFunctionObjArgs (fn, h->py_h, NULL); Py_DECREF (fn); check_python_failure ("close"); Py_XDECREF (r); } - Py_DECREF (obj); + Py_DECREF (h->py_h); + free (h); } static int64_t py_get_size (void *handle) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; int64_t ret; @@ -511,7 +527,7 @@ py_get_size (void *handle) PyErr_Clear (); - r = PyObject_CallFunctionObjArgs (fn, obj, NULL); + r = PyObject_CallFunctionObjArgs (fn, h->py_h, NULL); Py_DECREF (fn); if (check_python_failure ("get_size") == -1) return -1; @@ -528,7 +544,7 @@ static int py_pread (void *handle, void *buf, uint32_t count, uint64_t offset, uint32_t flags) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; Py_buffer view = {0}; @@ -543,10 +559,10 @@ py_pread (void *handle, void *buf, uint32_t count, uint64_t offset, switch (py_api_version) { case 1: - r = PyObject_CallFunction (fn, "OiL", obj, count, offset); + r = PyObject_CallFunction (fn, "OiL", h->py_h, count, offset); break; case 2: - r = PyObject_CallFunction (fn, "ONLI", obj, + r = PyObject_CallFunction (fn, "ONLI", h->py_h, PyMemoryView_FromMemory ((char *)buf, count, PyBUF_WRITE), offset, flags); break; @@ -590,7 +606,7 @@ static int py_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset, uint32_t flags) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; @@ -599,12 +615,12 @@ py_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset, switch (py_api_version) { case 1: - r = PyObject_CallFunction (fn, "ONL", obj, + r = PyObject_CallFunction (fn, "ONL", h->py_h, PyMemoryView_FromMemory ((char *)buf, count, PyBUF_READ), offset); break; case 2: - r = PyObject_CallFunction (fn, "ONLI", obj, + r = PyObject_CallFunction (fn, "ONLI", h->py_h, PyMemoryView_FromMemory ((char *)buf, count, PyBUF_READ), offset, flags); break; @@ -626,7 +642,7 @@ py_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset, static int py_flush (void *handle, uint32_t flags) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; @@ -635,10 +651,10 @@ py_flush (void *handle, uint32_t flags) switch (py_api_version) { case 1: - r = PyObject_CallFunctionObjArgs (fn, obj, NULL); + r = PyObject_CallFunctionObjArgs (fn, h->py_h, NULL); break; case 2: - r = PyObject_CallFunction (fn, "OI", obj, flags); + r = PyObject_CallFunction (fn, "OI", h->py_h, flags); break; default: abort (); } @@ -658,7 +674,7 @@ py_flush (void *handle, uint32_t flags) static int py_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; @@ -667,10 +683,10 @@ py_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags) switch (py_api_version) { case 1: - r = PyObject_CallFunction (fn, "OiL", obj, count, offset); + r = PyObject_CallFunction (fn, "OiL", h->py_h, count, offset); break; case 2: - r = PyObject_CallFunction (fn, "OiLI", obj, count, offset, flags); + r = PyObject_CallFunction (fn, "OiLI", h->py_h, count, offset, flags); break; default: abort (); } @@ -690,7 +706,7 @@ py_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags) static int py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; @@ -702,12 +718,12 @@ py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags) case 1: { int may_trim = flags & NBDKIT_FLAG_MAY_TRIM; r = PyObject_CallFunction (fn, "OiLO", - obj, count, offset, + h->py_h, count, offset, may_trim ? Py_True : Py_False); break; } case 2: - r = PyObject_CallFunction (fn, "OiLI", obj, count, offset, flags); + r = PyObject_CallFunction (fn, "OiLI", h->py_h, count, offset, flags); break; default: abort (); } @@ -736,7 +752,7 @@ py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags) static int py_cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; @@ -746,7 +762,7 @@ py_cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags) switch (py_api_version) { case 1: case 2: - r = PyObject_CallFunction (fn, "OiLI", obj, count, offset, flags, NULL); + r = PyObject_CallFunction (fn, "OiLI", h->py_h, count, offset, flags, NULL); break; default: abort (); } @@ -766,7 +782,7 @@ py_cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags) static int boolean_callback (void *handle, const char *can_fn, const char *plain_fn) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; int ret; @@ -774,7 +790,7 @@ boolean_callback (void *handle, const char *can_fn, const char *plain_fn) if (callback_defined (can_fn, &fn)) { PyErr_Clear (); - r = PyObject_CallFunctionObjArgs (fn, obj, NULL); + r = PyObject_CallFunctionObjArgs (fn, h->py_h, NULL); Py_DECREF (fn); if (check_python_failure (can_fn) == -1) return -1; @@ -825,19 +841,34 @@ py_can_trim (void *handle) static int py_can_zero (void *handle) { - return boolean_callback (handle, "can_zero", "zero"); + struct handle *h = handle; + + if (h->can_zero >= 0) + return h->can_zero; + return h->can_zero = boolean_callback (handle, "can_zero", "zero"); } static int py_can_fast_zero (void *handle) { - return boolean_callback (handle, "can_fast_zero", NULL); + int r; + + if (callback_defined ("can_fast_zero", NULL)) + return boolean_callback (handle, "can_fast_zero", NULL); + + /* No Python ‘can_fast_zero’, but we advertise fast fail support when + * 'can_zero' is false. (In C modules, nbdkit would do this). + */ + r = py_can_zero (handle); + if (r == -1) + return -1; + return !r; } static int py_can_fua (void *handle) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; int ret; @@ -845,7 +876,7 @@ py_can_fua (void *handle) if (callback_defined ("can_fua", &fn)) { PyErr_Clear (); - r = PyObject_CallFunctionObjArgs (fn, obj, NULL); + r = PyObject_CallFunctionObjArgs (fn, h->py_h, NULL); Py_DECREF (fn); if (check_python_failure ("can_fua") == -1) return -1; @@ -865,7 +896,7 @@ py_can_fua (void *handle) static int py_can_cache (void *handle) { - PyObject *obj = handle; + struct handle *h = handle; PyObject *fn; PyObject *r; int ret; @@ -873,7 +904,7 @@ py_can_cache (void *handle) if (callback_defined ("can_cache", &fn)) { PyErr_Clear (); - r = PyObject_CallFunctionObjArgs (fn, obj, NULL); + r = PyObject_CallFunctionObjArgs (fn, h->py_h, NULL); Py_DECREF (fn); if (check_python_failure ("can_cache") == -1) return -1; -- 2.25.1