diff options
| author | David Fries <David@Fries.net> | 2014-01-15 23:29:18 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-07 18:40:17 -0500 |
| commit | 9fcbbac5ded489c3a4e121343db999dd51cd6c75 (patch) | |
| tree | 1207602c8d7cc2da54784114a5d32d9ed6924b58 | |
| parent | 70b34d2ed807b722413894975a8c60617defb887 (diff) | |
w1: process w1 netlink commands in w1_process thread
Netlink is a socket interface and is expected to be asynchronous.
Clients can now make w1 requests without blocking by making use of the
w1_master thread to process netlink commands which was previously only
used for doing an automatic bus search.
Signed-off-by: David Fries <David@Fries.net>
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/w1/w1.c | 180 | ||||
| -rw-r--r-- | drivers/w1/w1.h | 32 | ||||
| -rw-r--r-- | drivers/w1/w1_int.c | 17 | ||||
| -rw-r--r-- | drivers/w1/w1_netlink.c | 166 |
4 files changed, 300 insertions, 95 deletions
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 97b35cb8b6da..53846c7f24ff 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c | |||
| @@ -79,19 +79,10 @@ static void w1_slave_release(struct device *dev) | |||
| 79 | { | 79 | { |
| 80 | struct w1_slave *sl = dev_to_w1_slave(dev); | 80 | struct w1_slave *sl = dev_to_w1_slave(dev); |
| 81 | 81 | ||
| 82 | dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name); | 82 | dev_dbg(dev, "%s: Releasing %s [%p]\n", __func__, sl->name, sl); |
| 83 | |||
| 84 | while (atomic_read(&sl->refcnt)) { | ||
| 85 | dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.\n", | ||
| 86 | sl->name, atomic_read(&sl->refcnt)); | ||
| 87 | if (msleep_interruptible(1000)) | ||
| 88 | flush_signals(current); | ||
| 89 | } | ||
| 90 | 83 | ||
| 91 | w1_family_put(sl->family); | 84 | w1_family_put(sl->family); |
| 92 | sl->master->slave_count--; | 85 | sl->master->slave_count--; |
| 93 | |||
| 94 | complete(&sl->released); | ||
| 95 | } | 86 | } |
| 96 | 87 | ||
| 97 | static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) | 88 | static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) |
| @@ -277,7 +268,6 @@ static ssize_t w1_master_attribute_store_pullup(struct device *dev, | |||
| 277 | mutex_lock(&md->mutex); | 268 | mutex_lock(&md->mutex); |
| 278 | md->enable_pullup = tmp; | 269 | md->enable_pullup = tmp; |
| 279 | mutex_unlock(&md->mutex); | 270 | mutex_unlock(&md->mutex); |
| 280 | wake_up_process(md->thread); | ||
| 281 | 271 | ||
| 282 | return count; | 272 | return count; |
| 283 | } | 273 | } |
| @@ -370,23 +360,20 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev, | |||
| 370 | { | 360 | { |
| 371 | struct w1_master *md = dev_to_w1_master(dev); | 361 | struct w1_master *md = dev_to_w1_master(dev); |
| 372 | int c = PAGE_SIZE; | 362 | int c = PAGE_SIZE; |
| 363 | struct list_head *ent, *n; | ||
| 364 | struct w1_slave *sl = NULL; | ||
| 373 | 365 | ||
| 374 | mutex_lock(&md->mutex); | 366 | mutex_lock(&md->list_mutex); |
| 375 | |||
| 376 | if (md->slave_count == 0) | ||
| 377 | c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n"); | ||
| 378 | else { | ||
| 379 | struct list_head *ent, *n; | ||
| 380 | struct w1_slave *sl; | ||
| 381 | 367 | ||
| 382 | list_for_each_safe(ent, n, &md->slist) { | 368 | list_for_each_safe(ent, n, &md->slist) { |
| 383 | sl = list_entry(ent, struct w1_slave, w1_slave_entry); | 369 | sl = list_entry(ent, struct w1_slave, w1_slave_entry); |
| 384 | 370 | ||
| 385 | c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name); | 371 | c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name); |
| 386 | } | ||
| 387 | } | 372 | } |
| 373 | if (!sl) | ||
| 374 | c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n"); | ||
| 388 | 375 | ||
| 389 | mutex_unlock(&md->mutex); | 376 | mutex_unlock(&md->list_mutex); |
| 390 | 377 | ||
| 391 | return PAGE_SIZE - c; | 378 | return PAGE_SIZE - c; |
| 392 | } | 379 | } |
| @@ -440,19 +427,22 @@ static int w1_atoreg_num(struct device *dev, const char *buf, size_t count, | |||
| 440 | } | 427 | } |
| 441 | 428 | ||
| 442 | /* Searches the slaves in the w1_master and returns a pointer or NULL. | 429 | /* Searches the slaves in the w1_master and returns a pointer or NULL. |
| 443 | * Note: must hold the mutex | 430 | * Note: must not hold list_mutex |
| 444 | */ | 431 | */ |
| 445 | struct w1_slave *w1_slave_search_device(struct w1_master *dev, | 432 | struct w1_slave *w1_slave_search_device(struct w1_master *dev, |
| 446 | struct w1_reg_num *rn) | 433 | struct w1_reg_num *rn) |
| 447 | { | 434 | { |
| 448 | struct w1_slave *sl; | 435 | struct w1_slave *sl; |
| 436 | mutex_lock(&dev->list_mutex); | ||
| 449 | list_for_each_entry(sl, &dev->slist, w1_slave_entry) { | 437 | list_for_each_entry(sl, &dev->slist, w1_slave_entry) { |
| 450 | if (sl->reg_num.family == rn->family && | 438 | if (sl->reg_num.family == rn->family && |
| 451 | sl->reg_num.id == rn->id && | 439 | sl->reg_num.id == rn->id && |
| 452 | sl->reg_num.crc == rn->crc) { | 440 | sl->reg_num.crc == rn->crc) { |
| 441 | mutex_unlock(&dev->list_mutex); | ||
| 453 | return sl; | 442 | return sl; |
| 454 | } | 443 | } |
| 455 | } | 444 | } |
| 445 | mutex_unlock(&dev->list_mutex); | ||
| 456 | return NULL; | 446 | return NULL; |
| 457 | } | 447 | } |
| 458 | 448 | ||
| @@ -509,7 +499,10 @@ static ssize_t w1_master_attribute_store_remove(struct device *dev, | |||
| 509 | mutex_lock(&md->mutex); | 499 | mutex_lock(&md->mutex); |
| 510 | sl = w1_slave_search_device(md, &rn); | 500 | sl = w1_slave_search_device(md, &rn); |
| 511 | if (sl) { | 501 | if (sl) { |
| 512 | w1_slave_detach(sl); | 502 | result = w1_slave_detach(sl); |
| 503 | /* refcnt 0 means it was detached in the call */ | ||
| 504 | if (result == 0) | ||
| 505 | result = count; | ||
| 513 | } else { | 506 | } else { |
| 514 | dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family, | 507 | dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family, |
| 515 | (unsigned long long)rn.id); | 508 | (unsigned long long)rn.id); |
| @@ -704,7 +697,9 @@ static int __w1_attach_slave_device(struct w1_slave *sl) | |||
| 704 | dev_set_uevent_suppress(&sl->dev, false); | 697 | dev_set_uevent_suppress(&sl->dev, false); |
| 705 | kobject_uevent(&sl->dev.kobj, KOBJ_ADD); | 698 | kobject_uevent(&sl->dev.kobj, KOBJ_ADD); |
| 706 | 699 | ||
| 700 | mutex_lock(&sl->master->list_mutex); | ||
| 707 | list_add_tail(&sl->w1_slave_entry, &sl->master->slist); | 701 | list_add_tail(&sl->w1_slave_entry, &sl->master->slist); |
| 702 | mutex_unlock(&sl->master->list_mutex); | ||
| 708 | 703 | ||
| 709 | return 0; | 704 | return 0; |
| 710 | } | 705 | } |
| @@ -731,8 +726,8 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) | |||
| 731 | 726 | ||
| 732 | memset(&msg, 0, sizeof(msg)); | 727 | memset(&msg, 0, sizeof(msg)); |
| 733 | memcpy(&sl->reg_num, rn, sizeof(sl->reg_num)); | 728 | memcpy(&sl->reg_num, rn, sizeof(sl->reg_num)); |
| 734 | atomic_set(&sl->refcnt, 0); | 729 | atomic_set(&sl->refcnt, 1); |
| 735 | init_completion(&sl->released); | 730 | atomic_inc(&sl->master->refcnt); |
| 736 | 731 | ||
| 737 | /* slave modules need to be loaded in a context with unlocked mutex */ | 732 | /* slave modules need to be loaded in a context with unlocked mutex */ |
| 738 | mutex_unlock(&dev->mutex); | 733 | mutex_unlock(&dev->mutex); |
| @@ -772,23 +767,48 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) | |||
| 772 | return 0; | 767 | return 0; |
| 773 | } | 768 | } |
| 774 | 769 | ||
| 775 | void w1_slave_detach(struct w1_slave *sl) | 770 | int w1_unref_slave(struct w1_slave *sl) |
| 776 | { | 771 | { |
| 777 | struct w1_netlink_msg msg; | 772 | struct w1_master *dev = sl->master; |
| 778 | 773 | int refcnt; | |
| 779 | dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, sl->name, sl); | 774 | mutex_lock(&dev->list_mutex); |
| 780 | 775 | refcnt = atomic_sub_return(1, &sl->refcnt); | |
| 781 | list_del(&sl->w1_slave_entry); | 776 | if (refcnt == 0) { |
| 782 | 777 | struct w1_netlink_msg msg; | |
| 783 | memset(&msg, 0, sizeof(msg)); | 778 | |
| 784 | memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); | 779 | dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, |
| 785 | msg.type = W1_SLAVE_REMOVE; | 780 | sl->name, sl); |
| 786 | w1_netlink_send(sl->master, &msg); | 781 | |
| 787 | 782 | list_del(&sl->w1_slave_entry); | |
| 788 | device_unregister(&sl->dev); | 783 | |
| 784 | memset(&msg, 0, sizeof(msg)); | ||
| 785 | memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); | ||
| 786 | msg.type = W1_SLAVE_REMOVE; | ||
| 787 | w1_netlink_send(sl->master, &msg); | ||
| 788 | |||
| 789 | device_unregister(&sl->dev); | ||
| 790 | #ifdef DEBUG | ||
| 791 | memset(sl, 0, sizeof(*sl)); | ||
| 792 | #endif | ||
| 793 | kfree(sl); | ||
| 794 | } | ||
| 795 | atomic_dec(&dev->refcnt); | ||
| 796 | mutex_unlock(&dev->list_mutex); | ||
| 797 | return refcnt; | ||
| 798 | } | ||
| 789 | 799 | ||
| 790 | wait_for_completion(&sl->released); | 800 | int w1_slave_detach(struct w1_slave *sl) |
| 791 | kfree(sl); | 801 | { |
| 802 | /* Only detach a slave once as it decreases the refcnt each time. */ | ||
| 803 | int destroy_now; | ||
| 804 | mutex_lock(&sl->master->list_mutex); | ||
| 805 | destroy_now = !test_bit(W1_SLAVE_DETACH, &sl->flags); | ||
| 806 | set_bit(W1_SLAVE_DETACH, &sl->flags); | ||
| 807 | mutex_unlock(&sl->master->list_mutex); | ||
| 808 | |||
| 809 | if (destroy_now) | ||
| 810 | destroy_now = !w1_unref_slave(sl); | ||
| 811 | return destroy_now ? 0 : -EBUSY; | ||
| 792 | } | 812 | } |
| 793 | 813 | ||
| 794 | struct w1_master *w1_search_master_id(u32 id) | 814 | struct w1_master *w1_search_master_id(u32 id) |
| @@ -817,7 +837,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id) | |||
| 817 | 837 | ||
| 818 | mutex_lock(&w1_mlock); | 838 | mutex_lock(&w1_mlock); |
| 819 | list_for_each_entry(dev, &w1_masters, w1_master_entry) { | 839 | list_for_each_entry(dev, &w1_masters, w1_master_entry) { |
| 820 | mutex_lock(&dev->mutex); | 840 | mutex_lock(&dev->list_mutex); |
| 821 | list_for_each_entry(sl, &dev->slist, w1_slave_entry) { | 841 | list_for_each_entry(sl, &dev->slist, w1_slave_entry) { |
| 822 | if (sl->reg_num.family == id->family && | 842 | if (sl->reg_num.family == id->family && |
| 823 | sl->reg_num.id == id->id && | 843 | sl->reg_num.id == id->id && |
| @@ -828,7 +848,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id) | |||
| 828 | break; | 848 | break; |
| 829 | } | 849 | } |
| 830 | } | 850 | } |
| 831 | mutex_unlock(&dev->mutex); | 851 | mutex_unlock(&dev->list_mutex); |
| 832 | 852 | ||
| 833 | if (found) | 853 | if (found) |
| 834 | break; | 854 | break; |
| @@ -848,6 +868,7 @@ void w1_reconnect_slaves(struct w1_family *f, int attach) | |||
| 848 | dev_dbg(&dev->dev, "Reconnecting slaves in device %s " | 868 | dev_dbg(&dev->dev, "Reconnecting slaves in device %s " |
| 849 | "for family %02x.\n", dev->name, f->fid); | 869 | "for family %02x.\n", dev->name, f->fid); |
| 850 | mutex_lock(&dev->mutex); | 870 | mutex_lock(&dev->mutex); |
| 871 | mutex_lock(&dev->list_mutex); | ||
| 851 | list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { | 872 | list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { |
| 852 | /* If it is a new family, slaves with the default | 873 | /* If it is a new family, slaves with the default |
| 853 | * family driver and are that family will be | 874 | * family driver and are that family will be |
| @@ -859,14 +880,19 @@ void w1_reconnect_slaves(struct w1_family *f, int attach) | |||
| 859 | (!attach && sl->family->fid == f->fid)) { | 880 | (!attach && sl->family->fid == f->fid)) { |
| 860 | struct w1_reg_num rn; | 881 | struct w1_reg_num rn; |
| 861 | 882 | ||
| 883 | mutex_unlock(&dev->list_mutex); | ||
| 862 | memcpy(&rn, &sl->reg_num, sizeof(rn)); | 884 | memcpy(&rn, &sl->reg_num, sizeof(rn)); |
| 863 | w1_slave_detach(sl); | 885 | /* If it was already in use let the automatic |
| 864 | 886 | * scan pick it up again later. | |
| 865 | w1_attach_slave_device(dev, &rn); | 887 | */ |
| 888 | if (!w1_slave_detach(sl)) | ||
| 889 | w1_attach_slave_device(dev, &rn); | ||
| 890 | mutex_lock(&dev->list_mutex); | ||
| 866 | } | 891 | } |
| 867 | } | 892 | } |
| 868 | dev_dbg(&dev->dev, "Reconnecting slaves in device %s " | 893 | dev_dbg(&dev->dev, "Reconnecting slaves in device %s " |
| 869 | "has been finished.\n", dev->name); | 894 | "has been finished.\n", dev->name); |
| 895 | mutex_unlock(&dev->list_mutex); | ||
| 870 | mutex_unlock(&dev->mutex); | 896 | mutex_unlock(&dev->mutex); |
| 871 | } | 897 | } |
| 872 | mutex_unlock(&w1_mlock); | 898 | mutex_unlock(&w1_mlock); |
| @@ -1020,17 +1046,24 @@ void w1_search_process_cb(struct w1_master *dev, u8 search_type, | |||
| 1020 | { | 1046 | { |
| 1021 | struct w1_slave *sl, *sln; | 1047 | struct w1_slave *sl, *sln; |
| 1022 | 1048 | ||
| 1049 | mutex_lock(&dev->list_mutex); | ||
| 1023 | list_for_each_entry(sl, &dev->slist, w1_slave_entry) | 1050 | list_for_each_entry(sl, &dev->slist, w1_slave_entry) |
| 1024 | clear_bit(W1_SLAVE_ACTIVE, &sl->flags); | 1051 | clear_bit(W1_SLAVE_ACTIVE, &sl->flags); |
| 1052 | mutex_unlock(&dev->list_mutex); | ||
| 1025 | 1053 | ||
| 1026 | w1_search_devices(dev, search_type, cb); | 1054 | w1_search_devices(dev, search_type, cb); |
| 1027 | 1055 | ||
| 1056 | mutex_lock(&dev->list_mutex); | ||
| 1028 | list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { | 1057 | list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { |
| 1029 | if (!test_bit(W1_SLAVE_ACTIVE, &sl->flags) && !--sl->ttl) | 1058 | if (!test_bit(W1_SLAVE_ACTIVE, &sl->flags) && !--sl->ttl) { |
| 1059 | mutex_unlock(&dev->list_mutex); | ||
| 1030 | w1_slave_detach(sl); | 1060 | w1_slave_detach(sl); |
| 1061 | mutex_lock(&dev->list_mutex); | ||
| 1062 | } | ||
| 1031 | else if (test_bit(W1_SLAVE_ACTIVE, &sl->flags)) | 1063 | else if (test_bit(W1_SLAVE_ACTIVE, &sl->flags)) |
| 1032 | sl->ttl = dev->slave_ttl; | 1064 | sl->ttl = dev->slave_ttl; |
| 1033 | } | 1065 | } |
| 1066 | mutex_unlock(&dev->list_mutex); | ||
| 1034 | 1067 | ||
| 1035 | if (dev->search_count > 0) | 1068 | if (dev->search_count > 0) |
| 1036 | dev->search_count--; | 1069 | dev->search_count--; |
| @@ -1041,6 +1074,26 @@ static void w1_search_process(struct w1_master *dev, u8 search_type) | |||
| 1041 | w1_search_process_cb(dev, search_type, w1_slave_found); | 1074 | w1_search_process_cb(dev, search_type, w1_slave_found); |
| 1042 | } | 1075 | } |
| 1043 | 1076 | ||
| 1077 | int w1_process_callbacks(struct w1_master *dev) | ||
| 1078 | { | ||
| 1079 | int ret = 0; | ||
| 1080 | struct w1_async_cmd *async_cmd, *async_n; | ||
| 1081 | |||
| 1082 | /* The list can be added to in another thread, loop until it is empty */ | ||
| 1083 | while (!list_empty(&dev->async_list)) { | ||
| 1084 | list_for_each_entry_safe(async_cmd, async_n, &dev->async_list, | ||
| 1085 | async_entry) { | ||
| 1086 | /* drop the lock, if it is a search it can take a long | ||
| 1087 | * time */ | ||
| 1088 | mutex_unlock(&dev->list_mutex); | ||
| 1089 | async_cmd->cb(dev, async_cmd); | ||
| 1090 | ret = 1; | ||
| 1091 | mutex_lock(&dev->list_mutex); | ||
| 1092 | } | ||
| 1093 | } | ||
| 1094 | return ret; | ||
| 1095 | } | ||
| 1096 | |||
| 1044 | int w1_process(void *data) | 1097 | int w1_process(void *data) |
| 1045 | { | 1098 | { |
| 1046 | struct w1_master *dev = (struct w1_master *) data; | 1099 | struct w1_master *dev = (struct w1_master *) data; |
| @@ -1048,23 +1101,46 @@ int w1_process(void *data) | |||
| 1048 | * time can be calculated in jiffies once. | 1101 | * time can be calculated in jiffies once. |
| 1049 | */ | 1102 | */ |
| 1050 | const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000); | 1103 | const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000); |
| 1104 | /* remainder if it woke up early */ | ||
| 1105 | unsigned long jremain = 0; | ||
| 1051 | 1106 | ||
| 1052 | while (!kthread_should_stop()) { | 1107 | for (;;) { |
| 1053 | if (dev->search_count) { | 1108 | |
| 1109 | if (!jremain && dev->search_count) { | ||
| 1054 | mutex_lock(&dev->mutex); | 1110 | mutex_lock(&dev->mutex); |
| 1055 | w1_search_process(dev, W1_SEARCH); | 1111 | w1_search_process(dev, W1_SEARCH); |
| 1056 | mutex_unlock(&dev->mutex); | 1112 | mutex_unlock(&dev->mutex); |
| 1057 | } | 1113 | } |
| 1058 | 1114 | ||
| 1115 | mutex_lock(&dev->list_mutex); | ||
| 1116 | /* Note, w1_process_callback drops the lock while processing, | ||
| 1117 | * but locks it again before returning. | ||
| 1118 | */ | ||
| 1119 | if (!w1_process_callbacks(dev) && jremain) { | ||
| 1120 | /* a wake up is either to stop the thread, process | ||
| 1121 | * callbacks, or search, it isn't process callbacks, so | ||
| 1122 | * schedule a search. | ||
| 1123 | */ | ||
| 1124 | jremain = 1; | ||
| 1125 | } | ||
| 1126 | |||
| 1059 | try_to_freeze(); | 1127 | try_to_freeze(); |
| 1060 | __set_current_state(TASK_INTERRUPTIBLE); | 1128 | __set_current_state(TASK_INTERRUPTIBLE); |
| 1061 | 1129 | ||
| 1130 | /* hold list_mutex until after interruptible to prevent loosing | ||
| 1131 | * the wakeup signal when async_cmd is added. | ||
| 1132 | */ | ||
| 1133 | mutex_unlock(&dev->list_mutex); | ||
| 1134 | |||
| 1062 | if (kthread_should_stop()) | 1135 | if (kthread_should_stop()) |
| 1063 | break; | 1136 | break; |
| 1064 | 1137 | ||
| 1065 | /* Only sleep when the search is active. */ | 1138 | /* Only sleep when the search is active. */ |
| 1066 | if (dev->search_count) | 1139 | if (dev->search_count) { |
| 1067 | schedule_timeout(jtime); | 1140 | if (!jremain) |
| 1141 | jremain = jtime; | ||
| 1142 | jremain = schedule_timeout(jremain); | ||
| 1143 | } | ||
| 1068 | else | 1144 | else |
| 1069 | schedule(); | 1145 | schedule(); |
| 1070 | } | 1146 | } |
diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index 3376bfbb10f4..a096ef40119e 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h | |||
| @@ -58,6 +58,7 @@ struct w1_reg_num | |||
| 58 | #define W1_RESUME_CMD 0xA5 | 58 | #define W1_RESUME_CMD 0xA5 |
| 59 | 59 | ||
| 60 | #define W1_SLAVE_ACTIVE 0 | 60 | #define W1_SLAVE_ACTIVE 0 |
| 61 | #define W1_SLAVE_DETACH 1 | ||
| 61 | 62 | ||
| 62 | struct w1_slave | 63 | struct w1_slave |
| 63 | { | 64 | { |
| @@ -74,7 +75,6 @@ struct w1_slave | |||
| 74 | struct w1_family *family; | 75 | struct w1_family *family; |
| 75 | void *family_data; | 76 | void *family_data; |
| 76 | struct device dev; | 77 | struct device dev; |
| 77 | struct completion released; | ||
| 78 | }; | 78 | }; |
| 79 | 79 | ||
| 80 | typedef void (*w1_slave_found_callback)(struct w1_master *, u64); | 80 | typedef void (*w1_slave_found_callback)(struct w1_master *, u64); |
| @@ -171,7 +171,14 @@ struct w1_master | |||
| 171 | struct list_head w1_master_entry; | 171 | struct list_head w1_master_entry; |
| 172 | struct module *owner; | 172 | struct module *owner; |
| 173 | unsigned char name[W1_MAXNAMELEN]; | 173 | unsigned char name[W1_MAXNAMELEN]; |
| 174 | /* list_mutex protects just slist and async_list so slaves can be | ||
| 175 | * searched for and async commands added while the master has | ||
| 176 | * w1_master.mutex locked and is operating on the bus. | ||
| 177 | * lock order w1_mlock, w1_master.mutex, w1_master_list_mutex | ||
| 178 | */ | ||
| 179 | struct mutex list_mutex; | ||
| 174 | struct list_head slist; | 180 | struct list_head slist; |
| 181 | struct list_head async_list; | ||
| 175 | int max_slave_count, slave_count; | 182 | int max_slave_count, slave_count; |
| 176 | unsigned long attempts; | 183 | unsigned long attempts; |
| 177 | int slave_ttl; | 184 | int slave_ttl; |
| @@ -205,11 +212,29 @@ struct w1_master | |||
| 205 | u32 seq; | 212 | u32 seq; |
| 206 | }; | 213 | }; |
| 207 | 214 | ||
| 215 | /** | ||
| 216 | * struct w1_async_cmd - execute callback from the w1_process kthread | ||
| 217 | * @async_entry: link entry | ||
| 218 | * @cb: callback function, must list_del and destroy this list before | ||
| 219 | * returning | ||
| 220 | * | ||
| 221 | * When inserted into the w1_master async_list, w1_process will execute | ||
| 222 | * the callback. Embed this into the structure with the command details. | ||
| 223 | */ | ||
| 224 | struct w1_async_cmd { | ||
| 225 | struct list_head async_entry; | ||
| 226 | void (*cb)(struct w1_master *dev, struct w1_async_cmd *async_cmd); | ||
| 227 | }; | ||
| 228 | |||
| 208 | int w1_create_master_attributes(struct w1_master *); | 229 | int w1_create_master_attributes(struct w1_master *); |
| 209 | void w1_destroy_master_attributes(struct w1_master *master); | 230 | void w1_destroy_master_attributes(struct w1_master *master); |
| 210 | void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); | 231 | void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); |
| 211 | void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); | 232 | void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); |
| 233 | /* call w1_unref_slave to release the reference counts w1_search_slave added */ | ||
| 212 | struct w1_slave *w1_search_slave(struct w1_reg_num *id); | 234 | struct w1_slave *w1_search_slave(struct w1_reg_num *id); |
| 235 | /* decrements the reference on sl->master and sl, and cleans up if zero | ||
| 236 | * returns the reference count after it has been decremented */ | ||
| 237 | int w1_unref_slave(struct w1_slave *sl); | ||
| 213 | void w1_slave_found(struct w1_master *dev, u64 rn); | 238 | void w1_slave_found(struct w1_master *dev, u64 rn); |
| 214 | void w1_search_process_cb(struct w1_master *dev, u8 search_type, | 239 | void w1_search_process_cb(struct w1_master *dev, u8 search_type, |
| 215 | w1_slave_found_callback cb); | 240 | w1_slave_found_callback cb); |
| @@ -224,7 +249,8 @@ struct w1_master *w1_search_master_id(u32 id); | |||
| 224 | */ | 249 | */ |
| 225 | void w1_reconnect_slaves(struct w1_family *f, int attach); | 250 | void w1_reconnect_slaves(struct w1_family *f, int attach); |
| 226 | int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn); | 251 | int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn); |
| 227 | void w1_slave_detach(struct w1_slave *sl); | 252 | /* 0 success, otherwise EBUSY */ |
| 253 | int w1_slave_detach(struct w1_slave *sl); | ||
| 228 | 254 | ||
| 229 | u8 w1_triplet(struct w1_master *dev, int bdir); | 255 | u8 w1_triplet(struct w1_master *dev, int bdir); |
| 230 | void w1_write_8(struct w1_master *, u8); | 256 | void w1_write_8(struct w1_master *, u8); |
| @@ -260,6 +286,8 @@ extern int w1_max_slave_ttl; | |||
| 260 | extern struct list_head w1_masters; | 286 | extern struct list_head w1_masters; |
| 261 | extern struct mutex w1_mlock; | 287 | extern struct mutex w1_mlock; |
| 262 | 288 | ||
| 289 | /* returns 1 if there were commands to executed 0 otherwise */ | ||
| 290 | extern int w1_process_callbacks(struct w1_master *dev); | ||
| 263 | extern int w1_process(void *); | 291 | extern int w1_process(void *); |
| 264 | 292 | ||
| 265 | #endif /* __KERNEL__ */ | 293 | #endif /* __KERNEL__ */ |
diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 423f3c2b9e7d..66b2caae48f3 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c | |||
| @@ -75,8 +75,10 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, | |||
| 75 | atomic_set(&dev->refcnt, 2); | 75 | atomic_set(&dev->refcnt, 2); |
| 76 | 76 | ||
| 77 | INIT_LIST_HEAD(&dev->slist); | 77 | INIT_LIST_HEAD(&dev->slist); |
| 78 | INIT_LIST_HEAD(&dev->async_list); | ||
| 78 | mutex_init(&dev->mutex); | 79 | mutex_init(&dev->mutex); |
| 79 | mutex_init(&dev->bus_mutex); | 80 | mutex_init(&dev->bus_mutex); |
| 81 | mutex_init(&dev->list_mutex); | ||
| 80 | 82 | ||
| 81 | memcpy(&dev->dev, device, sizeof(struct device)); | 83 | memcpy(&dev->dev, device, sizeof(struct device)); |
| 82 | dev_set_name(&dev->dev, "w1_bus_master%u", dev->id); | 84 | dev_set_name(&dev->dev, "w1_bus_master%u", dev->id); |
| @@ -188,17 +190,22 @@ void __w1_remove_master_device(struct w1_master *dev) | |||
| 188 | struct w1_netlink_msg msg; | 190 | struct w1_netlink_msg msg; |
| 189 | struct w1_slave *sl, *sln; | 191 | struct w1_slave *sl, *sln; |
| 190 | 192 | ||
| 191 | set_bit(W1_ABORT_SEARCH, &dev->flags); | ||
| 192 | kthread_stop(dev->thread); | ||
| 193 | |||
| 194 | mutex_lock(&w1_mlock); | 193 | mutex_lock(&w1_mlock); |
| 195 | list_del(&dev->w1_master_entry); | 194 | list_del(&dev->w1_master_entry); |
| 196 | mutex_unlock(&w1_mlock); | 195 | mutex_unlock(&w1_mlock); |
| 197 | 196 | ||
| 197 | set_bit(W1_ABORT_SEARCH, &dev->flags); | ||
| 198 | kthread_stop(dev->thread); | ||
| 199 | |||
| 198 | mutex_lock(&dev->mutex); | 200 | mutex_lock(&dev->mutex); |
| 199 | list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) | 201 | mutex_lock(&dev->list_mutex); |
| 202 | list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { | ||
| 203 | mutex_unlock(&dev->list_mutex); | ||
| 200 | w1_slave_detach(sl); | 204 | w1_slave_detach(sl); |
| 205 | mutex_lock(&dev->list_mutex); | ||
| 206 | } | ||
| 201 | w1_destroy_master_attributes(dev); | 207 | w1_destroy_master_attributes(dev); |
| 208 | mutex_unlock(&dev->list_mutex); | ||
| 202 | mutex_unlock(&dev->mutex); | 209 | mutex_unlock(&dev->mutex); |
| 203 | atomic_dec(&dev->refcnt); | 210 | atomic_dec(&dev->refcnt); |
| 204 | 211 | ||
| @@ -208,7 +215,9 @@ void __w1_remove_master_device(struct w1_master *dev) | |||
| 208 | 215 | ||
| 209 | if (msleep_interruptible(1000)) | 216 | if (msleep_interruptible(1000)) |
| 210 | flush_signals(current); | 217 | flush_signals(current); |
| 218 | w1_process_callbacks(dev); | ||
| 211 | } | 219 | } |
| 220 | w1_process_callbacks(dev); | ||
| 212 | 221 | ||
| 213 | memset(&msg, 0, sizeof(msg)); | 222 | memset(&msg, 0, sizeof(msg)); |
| 214 | msg.id.mst.id = dev->id; | 223 | msg.id.mst.id = dev->id; |
diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 747174be7b50..06d614af1166 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c | |||
| @@ -119,10 +119,12 @@ static int w1_get_slaves(struct w1_master *dev, | |||
| 119 | 119 | ||
| 120 | if (req_cmd->cmd == W1_CMD_LIST_SLAVES) { | 120 | if (req_cmd->cmd == W1_CMD_LIST_SLAVES) { |
| 121 | __u64 rn; | 121 | __u64 rn; |
| 122 | mutex_lock(&dev->list_mutex); | ||
| 122 | list_for_each_entry(sl, &dev->slist, w1_slave_entry) { | 123 | list_for_each_entry(sl, &dev->slist, w1_slave_entry) { |
| 123 | memcpy(&rn, &sl->reg_num, sizeof(rn)); | 124 | memcpy(&rn, &sl->reg_num, sizeof(rn)); |
| 124 | w1_send_slave(dev, rn); | 125 | w1_send_slave(dev, rn); |
| 125 | } | 126 | } |
| 127 | mutex_unlock(&dev->list_mutex); | ||
| 126 | } else { | 128 | } else { |
| 127 | w1_search_process_cb(dev, cmd->cmd == W1_CMD_ALARM_SEARCH ? | 129 | w1_search_process_cb(dev, cmd->cmd == W1_CMD_ALARM_SEARCH ? |
| 128 | W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave); | 130 | W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave); |
| @@ -368,29 +370,134 @@ static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rm | |||
| 368 | return error; | 370 | return error; |
| 369 | } | 371 | } |
| 370 | 372 | ||
| 373 | /* Bundle together a reference count, the full message, and broken out | ||
| 374 | * commands to be executed on each w1 master kthread in one memory allocation. | ||
| 375 | */ | ||
| 376 | struct w1_cb_block { | ||
| 377 | atomic_t refcnt; | ||
| 378 | struct cn_msg msg; | ||
| 379 | /* cn_msg data */ | ||
| 380 | /* one or more variable length struct w1_cb_node */ | ||
| 381 | }; | ||
| 382 | struct w1_cb_node { | ||
| 383 | struct w1_async_cmd async; | ||
| 384 | /* pointers within w1_cb_block and msg data */ | ||
| 385 | struct w1_cb_block *block; | ||
| 386 | struct w1_netlink_msg *m; | ||
| 387 | struct w1_slave *sl; | ||
| 388 | struct w1_master *dev; | ||
| 389 | }; | ||
| 390 | |||
| 391 | static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd) | ||
| 392 | { | ||
| 393 | struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node, | ||
| 394 | async); | ||
| 395 | u16 mlen = node->m->len; | ||
| 396 | u8 *cmd_data = node->m->data; | ||
| 397 | int err = 0; | ||
| 398 | struct w1_slave *sl = node->sl; | ||
| 399 | struct w1_netlink_cmd *cmd = NULL; | ||
| 400 | |||
| 401 | mutex_lock(&dev->mutex); | ||
| 402 | if (sl && w1_reset_select_slave(sl)) | ||
| 403 | err = -ENODEV; | ||
| 404 | |||
| 405 | while (mlen && !err) { | ||
| 406 | cmd = (struct w1_netlink_cmd *)cmd_data; | ||
| 407 | |||
| 408 | if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { | ||
| 409 | err = -E2BIG; | ||
| 410 | break; | ||
| 411 | } | ||
| 412 | |||
| 413 | if (sl) | ||
| 414 | err = w1_process_command_slave(sl, &node->block->msg, | ||
| 415 | node->m, cmd); | ||
| 416 | else | ||
| 417 | err = w1_process_command_master(dev, &node->block->msg, | ||
| 418 | node->m, cmd); | ||
| 419 | |||
| 420 | w1_netlink_send_error(&node->block->msg, node->m, cmd, err); | ||
| 421 | err = 0; | ||
| 422 | |||
| 423 | cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); | ||
| 424 | mlen -= cmd->len + sizeof(struct w1_netlink_cmd); | ||
| 425 | } | ||
| 426 | |||
| 427 | if (!cmd || err) | ||
| 428 | w1_netlink_send_error(&node->block->msg, node->m, cmd, err); | ||
| 429 | |||
| 430 | if (sl) | ||
| 431 | w1_unref_slave(sl); | ||
| 432 | else | ||
| 433 | atomic_dec(&dev->refcnt); | ||
| 434 | mutex_unlock(&dev->mutex); | ||
| 435 | |||
| 436 | mutex_lock(&dev->list_mutex); | ||
| 437 | list_del(&async_cmd->async_entry); | ||
| 438 | mutex_unlock(&dev->list_mutex); | ||
| 439 | |||
| 440 | if (atomic_sub_return(1, &node->block->refcnt) == 0) | ||
| 441 | kfree(node->block); | ||
| 442 | } | ||
| 443 | |||
| 371 | static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) | 444 | static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) |
| 372 | { | 445 | { |
| 373 | struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1); | 446 | struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1); |
| 374 | struct w1_netlink_cmd *cmd; | ||
| 375 | struct w1_slave *sl; | 447 | struct w1_slave *sl; |
| 376 | struct w1_master *dev; | 448 | struct w1_master *dev; |
| 449 | u16 msg_len; | ||
| 377 | int err = 0; | 450 | int err = 0; |
| 451 | struct w1_cb_block *block = NULL; | ||
| 452 | struct w1_cb_node *node = NULL; | ||
| 453 | int node_count = 0; | ||
| 454 | |||
| 455 | /* Count the number of master or slave commands there are to allocate | ||
| 456 | * space for one cb_node each. | ||
| 457 | */ | ||
| 458 | msg_len = msg->len; | ||
| 459 | while (msg_len && !err) { | ||
| 460 | if (m->len + sizeof(struct w1_netlink_msg) > msg_len) { | ||
| 461 | err = -E2BIG; | ||
| 462 | break; | ||
| 463 | } | ||
| 464 | |||
| 465 | if (m->type == W1_MASTER_CMD || m->type == W1_SLAVE_CMD) | ||
| 466 | ++node_count; | ||
| 467 | |||
| 468 | msg_len -= sizeof(struct w1_netlink_msg) + m->len; | ||
| 469 | m = (struct w1_netlink_msg *)(((u8 *)m) + | ||
| 470 | sizeof(struct w1_netlink_msg) + m->len); | ||
| 471 | } | ||
| 472 | m = (struct w1_netlink_msg *)(msg + 1); | ||
| 473 | if (node_count) { | ||
| 474 | /* msg->len doesn't include itself */ | ||
| 475 | long size = sizeof(struct w1_cb_block) + msg->len + | ||
| 476 | node_count*sizeof(struct w1_cb_node); | ||
| 477 | block = kmalloc(size, GFP_KERNEL); | ||
| 478 | if (!block) { | ||
| 479 | w1_netlink_send_error(msg, m, NULL, -ENOMEM); | ||
| 480 | return; | ||
| 481 | } | ||
| 482 | atomic_set(&block->refcnt, 1); | ||
| 483 | memcpy(&block->msg, msg, sizeof(*msg) + msg->len); | ||
| 484 | node = (struct w1_cb_node *)((u8 *)block->msg.data + msg->len); | ||
| 485 | } | ||
| 378 | 486 | ||
| 379 | while (msg->len && !err) { | 487 | msg_len = msg->len; |
| 488 | while (msg_len && !err) { | ||
| 380 | struct w1_reg_num id; | 489 | struct w1_reg_num id; |
| 381 | u16 mlen = m->len; | 490 | u16 mlen = m->len; |
| 382 | u8 *cmd_data = m->data; | ||
| 383 | 491 | ||
| 384 | dev = NULL; | 492 | dev = NULL; |
| 385 | sl = NULL; | 493 | sl = NULL; |
| 386 | cmd = NULL; | ||
| 387 | 494 | ||
| 388 | memcpy(&id, m->id.id, sizeof(id)); | 495 | memcpy(&id, m->id.id, sizeof(id)); |
| 389 | #if 0 | 496 | #if 0 |
| 390 | printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n", | 497 | printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n", |
| 391 | __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len); | 498 | __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len); |
| 392 | #endif | 499 | #endif |
| 393 | if (m->len + sizeof(struct w1_netlink_msg) > msg->len) { | 500 | if (m->len + sizeof(struct w1_netlink_msg) > msg_len) { |
| 394 | err = -E2BIG; | 501 | err = -E2BIG; |
| 395 | break; | 502 | break; |
| 396 | } | 503 | } |
| @@ -415,41 +522,24 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) | |||
| 415 | if (!mlen) | 522 | if (!mlen) |
| 416 | goto out_cont; | 523 | goto out_cont; |
| 417 | 524 | ||
| 418 | mutex_lock(&dev->mutex); | 525 | atomic_inc(&block->refcnt); |
| 526 | node->async.cb = w1_process_cb; | ||
| 527 | node->block = block; | ||
| 528 | node->m = (struct w1_netlink_msg *)((u8 *)&block->msg + | ||
| 529 | (size_t)((u8 *)m - (u8 *)msg)); | ||
| 530 | node->sl = sl; | ||
| 531 | node->dev = dev; | ||
| 419 | 532 | ||
| 420 | if (sl && w1_reset_select_slave(sl)) { | 533 | mutex_lock(&dev->list_mutex); |
| 421 | err = -ENODEV; | 534 | list_add_tail(&node->async.async_entry, &dev->async_list); |
| 422 | goto out_up; | 535 | wake_up_process(dev->thread); |
| 423 | } | 536 | mutex_unlock(&dev->list_mutex); |
| 424 | 537 | ++node; | |
| 425 | while (mlen) { | ||
| 426 | cmd = (struct w1_netlink_cmd *)cmd_data; | ||
| 427 | |||
| 428 | if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { | ||
| 429 | err = -E2BIG; | ||
| 430 | break; | ||
| 431 | } | ||
| 432 | |||
| 433 | if (sl) | ||
| 434 | err = w1_process_command_slave(sl, msg, m, cmd); | ||
| 435 | else | ||
| 436 | err = w1_process_command_master(dev, msg, m, cmd); | ||
| 437 | 538 | ||
| 438 | w1_netlink_send_error(msg, m, cmd, err); | ||
| 439 | err = 0; | ||
| 440 | |||
| 441 | cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); | ||
| 442 | mlen -= cmd->len + sizeof(struct w1_netlink_cmd); | ||
| 443 | } | ||
| 444 | out_up: | ||
| 445 | atomic_dec(&dev->refcnt); | ||
| 446 | if (sl) | ||
| 447 | atomic_dec(&sl->refcnt); | ||
| 448 | mutex_unlock(&dev->mutex); | ||
| 449 | out_cont: | 539 | out_cont: |
| 450 | if (!cmd || err) | 540 | if (err) |
| 451 | w1_netlink_send_error(msg, m, cmd, err); | 541 | w1_netlink_send_error(msg, m, NULL, err); |
| 452 | msg->len -= sizeof(struct w1_netlink_msg) + m->len; | 542 | msg_len -= sizeof(struct w1_netlink_msg) + m->len; |
| 453 | m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); | 543 | m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); |
| 454 | 544 | ||
| 455 | /* | 545 | /* |
| @@ -458,6 +548,8 @@ out_cont: | |||
| 458 | if (err == -ENODEV) | 548 | if (err == -ENODEV) |
| 459 | err = 0; | 549 | err = 0; |
| 460 | } | 550 | } |
| 551 | if (block && atomic_sub_return(1, &block->refcnt) == 0) | ||
| 552 | kfree(block); | ||
| 461 | } | 553 | } |
| 462 | 554 | ||
| 463 | int w1_init_netlink(void) | 555 | int w1_init_netlink(void) |
