A set of patches for a deadlock on "rbd map" error path and a fix

for invalid pointer dereference and uninitialized variable use on
 asynchronous create and unlink error paths.
 -----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCAAxFiEEydHwtzie9C7TfviiSn/eOAIR84sFAl6YkKMTHGlkcnlvbW92
 QGdtYWlsLmNvbQAKCRBKf944AhHzi9mfCACM7yEZA3rYEUzoUVO2MfaZOnbPVyFe
 0tRZB2Fcu5nzJLibeTMX8e0OKb0KtEpPcJXw8EMIe/IRA4ahUUCHp7cCe+jIoPuX
 OB9JLOD0tgQJ1jt7hAd7SZFkN/iCJ/jpF/9kSD/8cLHUmPy2g2QzUtSeEtuRfsXD
 8jOxW9heOIFVpysUC8HHsRO+b7yPL8AguG8WXNoDItL9uB1DmrgkxOhh/ijqPxVz
 F9Du3WlEPzdOTheU6pxtTAMdds4mq3ltBnUElCevR4qY0og4YaqDwnGf0pJlzSuN
 nVvAhSSOGbVdvkjzTaPo2BF5rEYXNm6Hln0HGHsUubnDlFZ200GbFEJk
 =b1jf
 -----END PGP SIGNATURE-----

Merge tag 'ceph-for-5.7-rc2' of git://github.com/ceph/ceph-client

Pull ceph fixes from Ilya Dryomov:

 - a set of patches for a deadlock on "rbd map" error path

 - a fix for invalid pointer dereference and uninitialized variable use
   on asynchronous create and unlink error paths.

* tag 'ceph-for-5.7-rc2' of git://github.com/ceph/ceph-client:
  ceph: fix potential bad pointer deref in async dirops cb's
  rbd: don't mess with a page vector in rbd_notify_op_lock()
  rbd: don't test rbd_dev->opts in rbd_dev_image_release()
  rbd: call rbd_dev_unprobe() after unwatching and flushing notifies
  rbd: avoid a deadlock on header_rwsem when flushing notifies
This commit is contained in:
Linus Torvalds 2020-04-16 10:29:34 -07:00
commit 3fa84bf926
4 changed files with 24 additions and 19 deletions

View File

@ -3754,11 +3754,7 @@ static int __rbd_notify_op_lock(struct rbd_device *rbd_dev,
static void rbd_notify_op_lock(struct rbd_device *rbd_dev, static void rbd_notify_op_lock(struct rbd_device *rbd_dev,
enum rbd_notify_op notify_op) enum rbd_notify_op notify_op)
{ {
struct page **reply_pages; __rbd_notify_op_lock(rbd_dev, notify_op, NULL, NULL);
size_t reply_len;
__rbd_notify_op_lock(rbd_dev, notify_op, &reply_pages, &reply_len);
ceph_release_page_vector(reply_pages, calc_pages_for(0, reply_len));
} }
static void rbd_notify_acquired_lock(struct work_struct *work) static void rbd_notify_acquired_lock(struct work_struct *work)
@ -4527,6 +4523,10 @@ static void cancel_tasks_sync(struct rbd_device *rbd_dev)
cancel_work_sync(&rbd_dev->unlock_work); cancel_work_sync(&rbd_dev->unlock_work);
} }
/*
* header_rwsem must not be held to avoid a deadlock with
* rbd_dev_refresh() when flushing notifies.
*/
static void rbd_unregister_watch(struct rbd_device *rbd_dev) static void rbd_unregister_watch(struct rbd_device *rbd_dev)
{ {
cancel_tasks_sync(rbd_dev); cancel_tasks_sync(rbd_dev);
@ -6894,9 +6894,10 @@ static void rbd_print_dne(struct rbd_device *rbd_dev, bool is_snap)
static void rbd_dev_image_release(struct rbd_device *rbd_dev) static void rbd_dev_image_release(struct rbd_device *rbd_dev)
{ {
rbd_dev_unprobe(rbd_dev); if (!rbd_is_ro(rbd_dev))
if (rbd_dev->opts)
rbd_unregister_watch(rbd_dev); rbd_unregister_watch(rbd_dev);
rbd_dev_unprobe(rbd_dev);
rbd_dev->image_format = 0; rbd_dev->image_format = 0;
kfree(rbd_dev->spec->image_id); kfree(rbd_dev->spec->image_id);
rbd_dev->spec->image_id = NULL; rbd_dev->spec->image_id = NULL;
@ -6907,6 +6908,9 @@ static void rbd_dev_image_release(struct rbd_device *rbd_dev)
* device. If this image is the one being mapped (i.e., not a * device. If this image is the one being mapped (i.e., not a
* parent), initiate a watch on its header object before using that * parent), initiate a watch on its header object before using that
* object to get detailed information about the rbd image. * object to get detailed information about the rbd image.
*
* On success, returns with header_rwsem held for write if called
* with @depth == 0.
*/ */
static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth) static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
{ {
@ -6936,11 +6940,14 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
} }
} }
if (!depth)
down_write(&rbd_dev->header_rwsem);
ret = rbd_dev_header_info(rbd_dev); ret = rbd_dev_header_info(rbd_dev);
if (ret) { if (ret) {
if (ret == -ENOENT && !need_watch) if (ret == -ENOENT && !need_watch)
rbd_print_dne(rbd_dev, false); rbd_print_dne(rbd_dev, false);
goto err_out_watch; goto err_out_probe;
} }
/* /*
@ -6985,10 +6992,11 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
return 0; return 0;
err_out_probe: err_out_probe:
rbd_dev_unprobe(rbd_dev); if (!depth)
err_out_watch: up_write(&rbd_dev->header_rwsem);
if (need_watch) if (need_watch)
rbd_unregister_watch(rbd_dev); rbd_unregister_watch(rbd_dev);
rbd_dev_unprobe(rbd_dev);
err_out_format: err_out_format:
rbd_dev->image_format = 0; rbd_dev->image_format = 0;
kfree(rbd_dev->spec->image_id); kfree(rbd_dev->spec->image_id);
@ -7050,12 +7058,9 @@ static ssize_t do_rbd_add(struct bus_type *bus,
goto err_out_rbd_dev; goto err_out_rbd_dev;
} }
down_write(&rbd_dev->header_rwsem);
rc = rbd_dev_image_probe(rbd_dev, 0); rc = rbd_dev_image_probe(rbd_dev, 0);
if (rc < 0) { if (rc < 0)
up_write(&rbd_dev->header_rwsem);
goto err_out_rbd_dev; goto err_out_rbd_dev;
}
if (rbd_dev->opts->alloc_size > rbd_dev->layout.object_size) { if (rbd_dev->opts->alloc_size > rbd_dev->layout.object_size) {
rbd_warn(rbd_dev, "alloc_size adjusted to %u", rbd_warn(rbd_dev, "alloc_size adjusted to %u",

View File

@ -1051,8 +1051,8 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc,
/* If op failed, mark everyone involved for errors */ /* If op failed, mark everyone involved for errors */
if (result) { if (result) {
int pathlen; int pathlen = 0;
u64 base; u64 base = 0;
char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen, char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
&base, 0); &base, 0);

View File

@ -527,8 +527,8 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,
if (result) { if (result) {
struct dentry *dentry = req->r_dentry; struct dentry *dentry = req->r_dentry;
int pathlen; int pathlen = 0;
u64 base; u64 base = 0;
char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen, char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
&base, 0); &base, 0);

View File

@ -521,7 +521,7 @@ extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc);
static inline void ceph_mdsc_free_path(char *path, int len) static inline void ceph_mdsc_free_path(char *path, int len)
{ {
if (path) if (!IS_ERR_OR_NULL(path))
__putname(path - (PATH_MAX - 1 - len)); __putname(path - (PATH_MAX - 1 - len));
} }