Commit 02cde50b7ea74557d32ff778c73809322445ccd2
Committed by
Alasdair G Kergon
1 parent
e2914cc26b
Exists in
master
and in
20 other branches
dm ioctl: optimize functions without variable params
Device-mapper ioctls receive and send data in a buffer supplied by userspace. The buffer has two parts. The first part contains a 'struct dm_ioctl' and has a fixed size. The second part depends on the ioctl and has a variable size. This patch recognises the specific ioctls that do not use the variable part of the buffer and skips allocating memory for it. In particular, when a device is suspended and a resume ioctl is sent, this now avoid memory allocation completely. The variable "struct dm_ioctl tmp" is moved from the function copy_params to its caller ctl_ioctl and renamed to param_kernel. It is used directly when the ioctl function doesn't need any arguments. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Showing 2 changed files with 37 additions and 21 deletions Side-by-side Diff
drivers/md/dm-ioctl.c
... | ... | @@ -1560,7 +1560,8 @@ |
1560 | 1560 | return r; |
1561 | 1561 | } |
1562 | 1562 | |
1563 | -#define DM_PARAMS_VMALLOC 0x0001 /* Params alloced with vmalloc not kmalloc */ | |
1563 | +#define DM_PARAMS_KMALLOC 0x0001 /* Params alloced with kmalloc */ | |
1564 | +#define DM_PARAMS_VMALLOC 0x0002 /* Params alloced with vmalloc */ | |
1564 | 1565 | #define DM_WIPE_BUFFER 0x0010 /* Wipe input buffer before returning from ioctl */ |
1565 | 1566 | |
1566 | 1567 | static void free_params(struct dm_ioctl *param, size_t param_size, int param_flags) |
1567 | 1568 | |
1568 | 1569 | |
1569 | 1570 | |
1570 | 1571 | |
1571 | 1572 | |
1572 | 1573 | |
1573 | 1574 | |
1574 | 1575 | |
1575 | 1576 | |
1576 | 1577 | |
1577 | 1578 | |
1578 | 1579 | |
1579 | 1580 | |
1580 | 1581 | |
1581 | 1582 | |
1582 | 1583 | |
... | ... | @@ -1568,66 +1569,80 @@ |
1568 | 1569 | if (param_flags & DM_WIPE_BUFFER) |
1569 | 1570 | memset(param, 0, param_size); |
1570 | 1571 | |
1572 | + if (param_flags & DM_PARAMS_KMALLOC) | |
1573 | + kfree(param); | |
1571 | 1574 | if (param_flags & DM_PARAMS_VMALLOC) |
1572 | 1575 | vfree(param); |
1573 | - else | |
1574 | - kfree(param); | |
1575 | 1576 | } |
1576 | 1577 | |
1577 | -static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param, int *param_flags) | |
1578 | +static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel, | |
1579 | + int ioctl_flags, | |
1580 | + struct dm_ioctl **param, int *param_flags) | |
1578 | 1581 | { |
1579 | - struct dm_ioctl tmp, *dmi; | |
1582 | + struct dm_ioctl *dmi; | |
1580 | 1583 | int secure_data; |
1584 | + const size_t minimum_data_size = sizeof(*param_kernel) - sizeof(param_kernel->data); | |
1581 | 1585 | |
1582 | - if (copy_from_user(&tmp, user, sizeof(tmp) - sizeof(tmp.data))) | |
1586 | + if (copy_from_user(param_kernel, user, minimum_data_size)) | |
1583 | 1587 | return -EFAULT; |
1584 | 1588 | |
1585 | - if (tmp.data_size < (sizeof(tmp) - sizeof(tmp.data))) | |
1589 | + if (param_kernel->data_size < minimum_data_size) | |
1586 | 1590 | return -EINVAL; |
1587 | 1591 | |
1588 | - secure_data = tmp.flags & DM_SECURE_DATA_FLAG; | |
1592 | + secure_data = param_kernel->flags & DM_SECURE_DATA_FLAG; | |
1589 | 1593 | |
1590 | 1594 | *param_flags = secure_data ? DM_WIPE_BUFFER : 0; |
1591 | 1595 | |
1596 | + if (ioctl_flags & IOCTL_FLAGS_NO_PARAMS) { | |
1597 | + dmi = param_kernel; | |
1598 | + dmi->data_size = minimum_data_size; | |
1599 | + goto data_copied; | |
1600 | + } | |
1601 | + | |
1592 | 1602 | /* |
1593 | 1603 | * Try to avoid low memory issues when a device is suspended. |
1594 | 1604 | * Use kmalloc() rather than vmalloc() when we can. |
1595 | 1605 | */ |
1596 | 1606 | dmi = NULL; |
1597 | - if (tmp.data_size <= KMALLOC_MAX_SIZE) | |
1598 | - dmi = kmalloc(tmp.data_size, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); | |
1607 | + if (param_kernel->data_size <= KMALLOC_MAX_SIZE) { | |
1608 | + dmi = kmalloc(param_kernel->data_size, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); | |
1609 | + if (dmi) | |
1610 | + *param_flags |= DM_PARAMS_KMALLOC; | |
1611 | + } | |
1599 | 1612 | |
1600 | 1613 | if (!dmi) { |
1601 | - dmi = __vmalloc(tmp.data_size, GFP_NOIO | __GFP_REPEAT | __GFP_HIGH, PAGE_KERNEL); | |
1602 | - *param_flags |= DM_PARAMS_VMALLOC; | |
1614 | + dmi = __vmalloc(param_kernel->data_size, GFP_NOIO | __GFP_REPEAT | __GFP_HIGH, PAGE_KERNEL); | |
1615 | + if (dmi) | |
1616 | + *param_flags |= DM_PARAMS_VMALLOC; | |
1603 | 1617 | } |
1604 | 1618 | |
1605 | 1619 | if (!dmi) { |
1606 | - if (secure_data && clear_user(user, tmp.data_size)) | |
1620 | + if (secure_data && clear_user(user, param_kernel->data_size)) | |
1607 | 1621 | return -EFAULT; |
1608 | 1622 | return -ENOMEM; |
1609 | 1623 | } |
1610 | 1624 | |
1611 | - if (copy_from_user(dmi, user, tmp.data_size)) | |
1625 | + if (copy_from_user(dmi, user, param_kernel->data_size)) | |
1612 | 1626 | goto bad; |
1613 | 1627 | |
1628 | +data_copied: | |
1614 | 1629 | /* |
1615 | 1630 | * Abort if something changed the ioctl data while it was being copied. |
1616 | 1631 | */ |
1617 | - if (dmi->data_size != tmp.data_size) { | |
1632 | + if (dmi->data_size != param_kernel->data_size) { | |
1618 | 1633 | DMERR("rejecting ioctl: data size modified while processing parameters"); |
1619 | 1634 | goto bad; |
1620 | 1635 | } |
1621 | 1636 | |
1622 | 1637 | /* Wipe the user buffer so we do not return it to userspace */ |
1623 | - if (secure_data && clear_user(user, tmp.data_size)) | |
1638 | + if (secure_data && clear_user(user, param_kernel->data_size)) | |
1624 | 1639 | goto bad; |
1625 | 1640 | |
1626 | 1641 | *param = dmi; |
1627 | 1642 | return 0; |
1628 | 1643 | |
1629 | 1644 | bad: |
1630 | - free_params(dmi, tmp.data_size, *param_flags); | |
1645 | + free_params(dmi, param_kernel->data_size, *param_flags); | |
1631 | 1646 | |
1632 | 1647 | return -EFAULT; |
1633 | 1648 | } |
... | ... | @@ -1671,6 +1686,7 @@ |
1671 | 1686 | struct dm_ioctl *uninitialized_var(param); |
1672 | 1687 | ioctl_fn fn = NULL; |
1673 | 1688 | size_t input_param_size; |
1689 | + struct dm_ioctl param_kernel; | |
1674 | 1690 | |
1675 | 1691 | /* only root can play with this */ |
1676 | 1692 | if (!capable(CAP_SYS_ADMIN)) |
... | ... | @@ -1704,7 +1720,7 @@ |
1704 | 1720 | /* |
1705 | 1721 | * Copy the parameters into kernel space. |
1706 | 1722 | */ |
1707 | - r = copy_params(user, ¶m, ¶m_flags); | |
1723 | + r = copy_params(user, ¶m_kernel, ioctl_flags, ¶m, ¶m_flags); | |
1708 | 1724 | |
1709 | 1725 | if (r) |
1710 | 1726 | return r; |
include/uapi/linux/dm-ioctl.h
... | ... | @@ -267,9 +267,9 @@ |
267 | 267 | #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) |
268 | 268 | |
269 | 269 | #define DM_VERSION_MAJOR 4 |
270 | -#define DM_VERSION_MINOR 23 | |
271 | -#define DM_VERSION_PATCHLEVEL 1 | |
272 | -#define DM_VERSION_EXTRA "-ioctl (2012-12-18)" | |
270 | +#define DM_VERSION_MINOR 24 | |
271 | +#define DM_VERSION_PATCHLEVEL 0 | |
272 | +#define DM_VERSION_EXTRA "-ioctl (2013-01-15)" | |
273 | 273 | |
274 | 274 | /* Status bits */ |
275 | 275 | #define DM_READONLY_FLAG (1 << 0) /* In/Out */ |