diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2013-03-01 17:45:49 -0500 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2013-03-01 17:45:49 -0500 |
commit | 02cde50b7ea74557d32ff778c73809322445ccd2 (patch) | |
tree | 5021be59504bc5dc3e4fb247debcbb9a383b0dab /drivers/md | |
parent | e2914cc26bbca67fd30fff02c6777e8477fc8a6a (diff) |
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>
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/dm-ioctl.c | 52 |
1 files changed, 34 insertions, 18 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 9ae11b2994f8..7eb0682d574f 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
@@ -1560,7 +1560,8 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user) | |||
1560 | return r; | 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 | #define DM_WIPE_BUFFER 0x0010 /* Wipe input buffer before returning from ioctl */ | 1565 | #define DM_WIPE_BUFFER 0x0010 /* Wipe input buffer before returning from ioctl */ |
1565 | 1566 | ||
1566 | static void free_params(struct dm_ioctl *param, size_t param_size, int param_flags) | 1567 | static void free_params(struct dm_ioctl *param, size_t param_size, int param_flags) |
@@ -1568,66 +1569,80 @@ static void free_params(struct dm_ioctl *param, size_t param_size, int param_fla | |||
1568 | if (param_flags & DM_WIPE_BUFFER) | 1569 | if (param_flags & DM_WIPE_BUFFER) |
1569 | memset(param, 0, param_size); | 1570 | memset(param, 0, param_size); |
1570 | 1571 | ||
1572 | if (param_flags & DM_PARAMS_KMALLOC) | ||
1573 | kfree(param); | ||
1571 | if (param_flags & DM_PARAMS_VMALLOC) | 1574 | if (param_flags & DM_PARAMS_VMALLOC) |
1572 | vfree(param); | 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 | int secure_data; | 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 | return -EFAULT; | 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 | return -EINVAL; | 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 | *param_flags = secure_data ? DM_WIPE_BUFFER : 0; | 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 | * Try to avoid low memory issues when a device is suspended. | 1603 | * Try to avoid low memory issues when a device is suspended. |
1594 | * Use kmalloc() rather than vmalloc() when we can. | 1604 | * Use kmalloc() rather than vmalloc() when we can. |
1595 | */ | 1605 | */ |
1596 | dmi = NULL; | 1606 | dmi = NULL; |
1597 | if (tmp.data_size <= KMALLOC_MAX_SIZE) | 1607 | if (param_kernel->data_size <= KMALLOC_MAX_SIZE) { |
1598 | dmi = kmalloc(tmp.data_size, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); | 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 | if (!dmi) { | 1613 | if (!dmi) { |
1601 | dmi = __vmalloc(tmp.data_size, GFP_NOIO | __GFP_REPEAT | __GFP_HIGH, PAGE_KERNEL); | 1614 | dmi = __vmalloc(param_kernel->data_size, GFP_NOIO | __GFP_REPEAT | __GFP_HIGH, PAGE_KERNEL); |
1602 | *param_flags |= DM_PARAMS_VMALLOC; | 1615 | if (dmi) |
1616 | *param_flags |= DM_PARAMS_VMALLOC; | ||
1603 | } | 1617 | } |
1604 | 1618 | ||
1605 | if (!dmi) { | 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 | return -EFAULT; | 1621 | return -EFAULT; |
1608 | return -ENOMEM; | 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 | goto bad; | 1626 | goto bad; |
1613 | 1627 | ||
1628 | data_copied: | ||
1614 | /* | 1629 | /* |
1615 | * Abort if something changed the ioctl data while it was being copied. | 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 | DMERR("rejecting ioctl: data size modified while processing parameters"); | 1633 | DMERR("rejecting ioctl: data size modified while processing parameters"); |
1619 | goto bad; | 1634 | goto bad; |
1620 | } | 1635 | } |
1621 | 1636 | ||
1622 | /* Wipe the user buffer so we do not return it to userspace */ | 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 | goto bad; | 1639 | goto bad; |
1625 | 1640 | ||
1626 | *param = dmi; | 1641 | *param = dmi; |
1627 | return 0; | 1642 | return 0; |
1628 | 1643 | ||
1629 | bad: | 1644 | bad: |
1630 | free_params(dmi, tmp.data_size, *param_flags); | 1645 | free_params(dmi, param_kernel->data_size, *param_flags); |
1631 | 1646 | ||
1632 | return -EFAULT; | 1647 | return -EFAULT; |
1633 | } | 1648 | } |
@@ -1671,6 +1686,7 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user) | |||
1671 | struct dm_ioctl *uninitialized_var(param); | 1686 | struct dm_ioctl *uninitialized_var(param); |
1672 | ioctl_fn fn = NULL; | 1687 | ioctl_fn fn = NULL; |
1673 | size_t input_param_size; | 1688 | size_t input_param_size; |
1689 | struct dm_ioctl param_kernel; | ||
1674 | 1690 | ||
1675 | /* only root can play with this */ | 1691 | /* only root can play with this */ |
1676 | if (!capable(CAP_SYS_ADMIN)) | 1692 | if (!capable(CAP_SYS_ADMIN)) |
@@ -1704,7 +1720,7 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user) | |||
1704 | /* | 1720 | /* |
1705 | * Copy the parameters into kernel space. | 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 | if (r) | 1725 | if (r) |
1710 | return r; | 1726 | return r; |