diff options
Diffstat (limited to 'ipc/mqueue.c')
| -rw-r--r-- | ipc/mqueue.c | 104 |
1 files changed, 81 insertions, 23 deletions
diff --git a/ipc/mqueue.c b/ipc/mqueue.c index af1692556c52..8ce57691e7b6 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c | |||
| @@ -69,6 +69,7 @@ struct mqueue_inode_info { | |||
| 69 | wait_queue_head_t wait_q; | 69 | wait_queue_head_t wait_q; |
| 70 | 70 | ||
| 71 | struct rb_root msg_tree; | 71 | struct rb_root msg_tree; |
| 72 | struct posix_msg_tree_node *node_cache; | ||
| 72 | struct mq_attr attr; | 73 | struct mq_attr attr; |
| 73 | 74 | ||
| 74 | struct sigevent notify; | 75 | struct sigevent notify; |
| @@ -134,15 +135,20 @@ static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *info) | |||
| 134 | else | 135 | else |
| 135 | p = &(*p)->rb_right; | 136 | p = &(*p)->rb_right; |
| 136 | } | 137 | } |
| 137 | leaf = kzalloc(sizeof(*leaf), GFP_ATOMIC); | 138 | if (info->node_cache) { |
| 138 | if (!leaf) | 139 | leaf = info->node_cache; |
| 139 | return -ENOMEM; | 140 | info->node_cache = NULL; |
| 140 | rb_init_node(&leaf->rb_node); | 141 | } else { |
| 141 | INIT_LIST_HEAD(&leaf->msg_list); | 142 | leaf = kmalloc(sizeof(*leaf), GFP_ATOMIC); |
| 143 | if (!leaf) | ||
| 144 | return -ENOMEM; | ||
| 145 | rb_init_node(&leaf->rb_node); | ||
| 146 | INIT_LIST_HEAD(&leaf->msg_list); | ||
| 147 | info->qsize += sizeof(*leaf); | ||
| 148 | } | ||
| 142 | leaf->priority = msg->m_type; | 149 | leaf->priority = msg->m_type; |
| 143 | rb_link_node(&leaf->rb_node, parent, p); | 150 | rb_link_node(&leaf->rb_node, parent, p); |
| 144 | rb_insert_color(&leaf->rb_node, &info->msg_tree); | 151 | rb_insert_color(&leaf->rb_node, &info->msg_tree); |
| 145 | info->qsize += sizeof(struct posix_msg_tree_node); | ||
| 146 | insert_msg: | 152 | insert_msg: |
| 147 | info->attr.mq_curmsgs++; | 153 | info->attr.mq_curmsgs++; |
| 148 | info->qsize += msg->m_ts; | 154 | info->qsize += msg->m_ts; |
| @@ -177,13 +183,17 @@ try_again: | |||
| 177 | return NULL; | 183 | return NULL; |
| 178 | } | 184 | } |
| 179 | leaf = rb_entry(parent, struct posix_msg_tree_node, rb_node); | 185 | leaf = rb_entry(parent, struct posix_msg_tree_node, rb_node); |
| 180 | if (list_empty(&leaf->msg_list)) { | 186 | if (unlikely(list_empty(&leaf->msg_list))) { |
| 181 | pr_warn_once("Inconsistency in POSIX message queue, " | 187 | pr_warn_once("Inconsistency in POSIX message queue, " |
| 182 | "empty leaf node but we haven't implemented " | 188 | "empty leaf node but we haven't implemented " |
| 183 | "lazy leaf delete!\n"); | 189 | "lazy leaf delete!\n"); |
| 184 | rb_erase(&leaf->rb_node, &info->msg_tree); | 190 | rb_erase(&leaf->rb_node, &info->msg_tree); |
| 185 | info->qsize -= sizeof(struct posix_msg_tree_node); | 191 | if (info->node_cache) { |
| 186 | kfree(leaf); | 192 | info->qsize -= sizeof(*leaf); |
| 193 | kfree(leaf); | ||
| 194 | } else { | ||
| 195 | info->node_cache = leaf; | ||
| 196 | } | ||
| 187 | goto try_again; | 197 | goto try_again; |
| 188 | } else { | 198 | } else { |
| 189 | msg = list_first_entry(&leaf->msg_list, | 199 | msg = list_first_entry(&leaf->msg_list, |
| @@ -191,8 +201,12 @@ try_again: | |||
| 191 | list_del(&msg->m_list); | 201 | list_del(&msg->m_list); |
| 192 | if (list_empty(&leaf->msg_list)) { | 202 | if (list_empty(&leaf->msg_list)) { |
| 193 | rb_erase(&leaf->rb_node, &info->msg_tree); | 203 | rb_erase(&leaf->rb_node, &info->msg_tree); |
| 194 | info->qsize -= sizeof(struct posix_msg_tree_node); | 204 | if (info->node_cache) { |
| 195 | kfree(leaf); | 205 | info->qsize -= sizeof(*leaf); |
| 206 | kfree(leaf); | ||
| 207 | } else { | ||
| 208 | info->node_cache = leaf; | ||
| 209 | } | ||
| 196 | } | 210 | } |
| 197 | } | 211 | } |
| 198 | info->attr.mq_curmsgs--; | 212 | info->attr.mq_curmsgs--; |
| @@ -235,6 +249,7 @@ static struct inode *mqueue_get_inode(struct super_block *sb, | |||
| 235 | info->qsize = 0; | 249 | info->qsize = 0; |
| 236 | info->user = NULL; /* set when all is ok */ | 250 | info->user = NULL; /* set when all is ok */ |
| 237 | info->msg_tree = RB_ROOT; | 251 | info->msg_tree = RB_ROOT; |
| 252 | info->node_cache = NULL; | ||
| 238 | memset(&info->attr, 0, sizeof(info->attr)); | 253 | memset(&info->attr, 0, sizeof(info->attr)); |
| 239 | info->attr.mq_maxmsg = min(ipc_ns->mq_msg_max, | 254 | info->attr.mq_maxmsg = min(ipc_ns->mq_msg_max, |
| 240 | ipc_ns->mq_msg_default); | 255 | ipc_ns->mq_msg_default); |
| @@ -367,6 +382,7 @@ static void mqueue_evict_inode(struct inode *inode) | |||
| 367 | spin_lock(&info->lock); | 382 | spin_lock(&info->lock); |
| 368 | while ((msg = msg_get(info)) != NULL) | 383 | while ((msg = msg_get(info)) != NULL) |
| 369 | free_msg(msg); | 384 | free_msg(msg); |
| 385 | kfree(info->node_cache); | ||
| 370 | spin_unlock(&info->lock); | 386 | spin_unlock(&info->lock); |
| 371 | 387 | ||
| 372 | /* Total amount of bytes accounted for the mqueue */ | 388 | /* Total amount of bytes accounted for the mqueue */ |
| @@ -964,7 +980,8 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, | |||
| 964 | struct mqueue_inode_info *info; | 980 | struct mqueue_inode_info *info; |
| 965 | ktime_t expires, *timeout = NULL; | 981 | ktime_t expires, *timeout = NULL; |
| 966 | struct timespec ts; | 982 | struct timespec ts; |
| 967 | int ret; | 983 | struct posix_msg_tree_node *new_leaf = NULL; |
| 984 | int ret = 0; | ||
| 968 | 985 | ||
| 969 | if (u_abs_timeout) { | 986 | if (u_abs_timeout) { |
| 970 | int res = prepare_timeout(u_abs_timeout, &expires, &ts); | 987 | int res = prepare_timeout(u_abs_timeout, &expires, &ts); |
| @@ -1012,39 +1029,60 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, | |||
| 1012 | msg_ptr->m_ts = msg_len; | 1029 | msg_ptr->m_ts = msg_len; |
| 1013 | msg_ptr->m_type = msg_prio; | 1030 | msg_ptr->m_type = msg_prio; |
| 1014 | 1031 | ||
| 1032 | /* | ||
| 1033 | * msg_insert really wants us to have a valid, spare node struct so | ||
| 1034 | * it doesn't have to kmalloc a GFP_ATOMIC allocation, but it will | ||
| 1035 | * fall back to that if necessary. | ||
| 1036 | */ | ||
| 1037 | if (!info->node_cache) | ||
| 1038 | new_leaf = kmalloc(sizeof(*new_leaf), GFP_KERNEL); | ||
| 1039 | |||
| 1015 | spin_lock(&info->lock); | 1040 | spin_lock(&info->lock); |
| 1016 | 1041 | ||
| 1042 | if (!info->node_cache && new_leaf) { | ||
| 1043 | /* Save our speculative allocation into the cache */ | ||
| 1044 | rb_init_node(&new_leaf->rb_node); | ||
| 1045 | INIT_LIST_HEAD(&new_leaf->msg_list); | ||
| 1046 | info->node_cache = new_leaf; | ||
| 1047 | info->qsize += sizeof(*new_leaf); | ||
| 1048 | new_leaf = NULL; | ||
| 1049 | } else { | ||
| 1050 | kfree(new_leaf); | ||
| 1051 | } | ||
| 1052 | |||
| 1017 | if (info->attr.mq_curmsgs == info->attr.mq_maxmsg) { | 1053 | if (info->attr.mq_curmsgs == info->attr.mq_maxmsg) { |
| 1018 | if (filp->f_flags & O_NONBLOCK) { | 1054 | if (filp->f_flags & O_NONBLOCK) { |
| 1019 | spin_unlock(&info->lock); | ||
| 1020 | ret = -EAGAIN; | 1055 | ret = -EAGAIN; |
| 1021 | } else { | 1056 | } else { |
| 1022 | wait.task = current; | 1057 | wait.task = current; |
| 1023 | wait.msg = (void *) msg_ptr; | 1058 | wait.msg = (void *) msg_ptr; |
| 1024 | wait.state = STATE_NONE; | 1059 | wait.state = STATE_NONE; |
| 1025 | ret = wq_sleep(info, SEND, timeout, &wait); | 1060 | ret = wq_sleep(info, SEND, timeout, &wait); |
| 1061 | /* | ||
| 1062 | * wq_sleep must be called with info->lock held, and | ||
| 1063 | * returns with the lock released | ||
| 1064 | */ | ||
| 1065 | goto out_free; | ||
| 1026 | } | 1066 | } |
| 1027 | if (ret < 0) | ||
| 1028 | free_msg(msg_ptr); | ||
| 1029 | } else { | 1067 | } else { |
| 1030 | receiver = wq_get_first_waiter(info, RECV); | 1068 | receiver = wq_get_first_waiter(info, RECV); |
| 1031 | if (receiver) { | 1069 | if (receiver) { |
| 1032 | pipelined_send(info, msg_ptr, receiver); | 1070 | pipelined_send(info, msg_ptr, receiver); |
| 1033 | } else { | 1071 | } else { |
| 1034 | /* adds message to the queue */ | 1072 | /* adds message to the queue */ |
| 1035 | if (msg_insert(msg_ptr, info)) { | 1073 | ret = msg_insert(msg_ptr, info); |
| 1036 | free_msg(msg_ptr); | 1074 | if (ret) |
| 1037 | ret = -ENOMEM; | 1075 | goto out_unlock; |
| 1038 | spin_unlock(&info->lock); | ||
| 1039 | goto out_fput; | ||
| 1040 | } | ||
| 1041 | __do_notify(info); | 1076 | __do_notify(info); |
| 1042 | } | 1077 | } |
| 1043 | inode->i_atime = inode->i_mtime = inode->i_ctime = | 1078 | inode->i_atime = inode->i_mtime = inode->i_ctime = |
| 1044 | CURRENT_TIME; | 1079 | CURRENT_TIME; |
| 1045 | spin_unlock(&info->lock); | ||
| 1046 | ret = 0; | ||
| 1047 | } | 1080 | } |
| 1081 | out_unlock: | ||
| 1082 | spin_unlock(&info->lock); | ||
| 1083 | out_free: | ||
| 1084 | if (ret) | ||
| 1085 | free_msg(msg_ptr); | ||
| 1048 | out_fput: | 1086 | out_fput: |
| 1049 | fput(filp); | 1087 | fput(filp); |
| 1050 | out: | 1088 | out: |
| @@ -1063,6 +1101,7 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, | |||
| 1063 | struct ext_wait_queue wait; | 1101 | struct ext_wait_queue wait; |
| 1064 | ktime_t expires, *timeout = NULL; | 1102 | ktime_t expires, *timeout = NULL; |
