Received: by 2002:ac0:a594:0:0:0:0:0 with SMTP id m20-v6csp1404266imm; Tue, 15 May 2018 19:40:55 -0700 (PDT) X-Google-Smtp-Source: AB8JxZr5EorIWrFRFeFs8F7mOdL1vD2VM4VZVRhjaiI6u/SOy1fwv760wViXwyWH8v69tSGGJtME X-Received: by 2002:a17:902:82ca:: with SMTP id u10-v6mr17099516plz.160.1526438455908; Tue, 15 May 2018 19:40:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526438455; cv=none; d=google.com; s=arc-20160816; b=HkkFGOzF23ucErCOCf12AlfqtSwSOpJrAoSsGKsRydYAB/aCPY8OQpIyS4h5plybeP 33MzlH+VY0WK9izt6K+RNb2OSQDOEE/I04jxS1/9tXviM083nq4shc2QiTdKDjro+rpc H0wUZ4BuQFwi0WDTKES2pLce9Pq3oUrYVwaWhEBof+B0hGZC1FmymbFOZE04otPpXUqq SfW7wjRde+hkK7Z0yTBt4+DzREl0l2we+Cq1mAlEd3rTdlew+KFyu2QbWABO3SwVqM0G 82mg0LSywnZ4V0Iw2xQgnGTa455jviT5/kqX/u5nGH6NfNENDd/hf101WneJ/LVfrK8G QfPg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:to:from:dkim-signature:arc-authentication-results; bh=VVJtSj5FUS5q0mGLV07WuZJXuGXJpDbs4c61c7/zz0E=; b=HhWuDQcZllTJS67CpfQMIWkFsxj+jlKaev+2bRgALcjKN7768G3U4lN6VpmBYuHGVb YnN5oedImBXR+GeOLwoIT370eLFwBB7KWzGu9avAJdvnRHil3CHpP8QaSo0tuIe7oUt2 oLEbpWybUvxZAoaUONljnQXIMrIPcMfeYSOG9fQBPkzlhzM8mTdqYuu8QfSyZpPVHAPL zR0Iys2Bewf1tjJtd1wcbkJlqX9DtSWQCbcTRWSq8AeDrbtNIRQiCeW1bqS+HFpZswDf 23300aAyMoH+mU2va02GiuVXqX8xs+/PB2EH0XhC1Lv6SvwD+AEr86VDk0GAhACo/pep de7w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@oracle.com header.s=corp-2017-10-26 header.b=r0doHTKI; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=oracle.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s4-v6si1229478pgn.403.2018.05.15.19.40.40; Tue, 15 May 2018 19:40:55 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@oracle.com header.s=corp-2017-10-26 header.b=r0doHTKI; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=oracle.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752239AbeEPCk0 (ORCPT + 99 others); Tue, 15 May 2018 22:40:26 -0400 Received: from aserp2130.oracle.com ([141.146.126.79]:48402 "EHLO aserp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751489AbeEPCkW (ORCPT ); Tue, 15 May 2018 22:40:22 -0400 Received: from pps.filterd (aserp2130.oracle.com [127.0.0.1]) by aserp2130.oracle.com (8.16.0.22/8.16.0.22) with SMTP id w4G2aXVh063884; Wed, 16 May 2018 02:40:17 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : subject : date : message-id : in-reply-to : references; s=corp-2017-10-26; bh=VVJtSj5FUS5q0mGLV07WuZJXuGXJpDbs4c61c7/zz0E=; b=r0doHTKIIF3CShhKeROhC6uHYCzsL+9ndvHJSwyvegVTr2Y1wiQyEEKFm+2RXNXC71M7 whRM0/UsvG/7/svldvyg5cp/JHi4oNSQ2Fz20Xxb0jVfvmDEv6PwmqAXR+xakNy5rgj4 d39ajUFA1Ub0egFOBxSeMtttCAzF7SkeMYYxADJaj2R6EgTvnYgErs1uAQ9ge6j2OQwJ Mi/7MBsqjsEcUeGpZW47u3alN/HcJwVLIiHYvbryFQX8Wh2t4p7c+J1+bLHMLEz1/zWU uynqFeMPgbI4Z32Uck+ENd0wSQvOpzUMk9s/owhkrqI9qEca1ubcN0NkvXUbOtcyNBpO Ow== Received: from userv0021.oracle.com (userv0021.oracle.com [156.151.31.71]) by aserp2130.oracle.com with ESMTP id 2hxpvcsf47-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Wed, 16 May 2018 02:40:17 +0000 Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by userv0021.oracle.com (8.14.4/8.14.4) with ESMTP id w4G2eG3h020053 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Wed, 16 May 2018 02:40:16 GMT Received: from abhmp0003.oracle.com (abhmp0003.oracle.com [141.146.116.9]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id w4G2eGBp002815; Wed, 16 May 2018 02:40:16 GMT Received: from localhost.localdomain (/73.69.118.222) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Tue, 15 May 2018 19:40:15 -0700 From: Pavel Tatashin To: pasha.tatashin@oracle.com, steven.sistare@oracle.com, daniel.m.jordan@oracle.com, linux-kernel@vger.kernel.org, jeffrey.t.kirsher@intel.com, intel-wired-lan@lists.osuosl.org, netdev@vger.kernel.org, gregkh@linuxfoundation.org, alexander.duyck@gmail.com, tobin@apporbit.com, andy.shevchenko@gmail.com Subject: [PATCH v5 2/3] drivers core: prepare device_shutdown for multi-threading Date: Tue, 15 May 2018 22:40:03 -0400 Message-Id: <20180516024004.28977-3-pasha.tatashin@oracle.com> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180516024004.28977-1-pasha.tatashin@oracle.com> References: <20180516024004.28977-1-pasha.tatashin@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=5900 definitions=8894 signatures=668698 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1711220000 definitions=main-1805160023 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Do all the necessary refactoring to prepare device_shutdown() logic to be multi-threaded. Which includes: 1. Change the direction of traversing the list instead of going backward, we now go forward. 2. Children are shutdown recursively for each root device from bottom-up. 3. Functions that can be multi-threaded have _task() in their name. Signed-off-by: Pavel Tatashin --- drivers/base/core.c | 178 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 149 insertions(+), 29 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index ed189f6d1a2f..210b619931bc 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2102,6 +2102,59 @@ const char *device_get_devnode(struct device *dev, return *tmp = s; } +/* + * device_children_count - device children count + * @parent: parent struct device. + * + * Returns number of children for this device or 0 if none. + */ +static int device_children_count(struct device *parent) +{ + struct klist_iter i; + int children = 0; + + if (!parent->p) + return 0; + + klist_iter_init(&parent->p->klist_children, &i); + while (next_device(&i)) + children++; + klist_iter_exit(&i); + + return children; +} + +/* + * device_get_child_by_index - Return child using the provided index. + * @parent: parent struct device. + * @index: Index of the child, where 0 is the first child in the children list, + * and so on. + * + * Returns child or NULL if child with this index is not present. + */ +static struct device * +device_get_child_by_index(struct device *parent, int index) +{ + struct klist_iter i; + struct device *dev = NULL, *d; + int child_index = 0; + + if (!parent->p || index < 0) + return NULL; + + klist_iter_init(&parent->p->klist_children, &i); + while ((d = next_device(&i))) { + if (child_index == index) { + dev = d; + break; + } + child_index++; + } + klist_iter_exit(&i); + + return dev; +} + /** * device_for_each_child - device child iterator. * @parent: parent struct device. @@ -2794,50 +2847,117 @@ static void device_shutdown_one(struct device *dev) put_device(dev); } +/* + * Passed as an argument to device_shutdown_child_task(). + * child_next_index the next available child index. + * parent Parent device. + */ +struct device_shutdown_task_data { + atomic_t child_next_index; + struct device *parent; +}; + +static int device_shutdown_child_task(void *data); + +/* + * Shutdown device tree with root started in dev. If dev has no children + * simply shutdown only this device. If dev has children recursively shutdown + * children first, and only then the parent. + */ +static void device_shutdown_tree(struct device *dev) +{ + int children_count; + + device_lock(dev); + children_count = device_children_count(dev); + + if (children_count) { + struct device_shutdown_task_data tdata; + int i; + + atomic_set(&tdata.child_next_index, 0); + tdata.parent = dev; + + for (i = 0; i < children_count; i++) { + device_shutdown_child_task(&tdata); + } + } + device_shutdown_one(dev); + device_unlock(dev); +} + +/* + * Only devices with parent are going through this function. The parent is + * locked and waits for all of its children to finish shutting down before + * calling shutdown function for itself. + */ +static int device_shutdown_child_task(void *data) +{ + struct device_shutdown_task_data *tdata = data; + int cidx = atomic_inc_return(&tdata->child_next_index) - 1; + struct device *dev = device_get_child_by_index(tdata->parent, cidx); + + /* ref. counter is going to be decremented in device_shutdown_one() */ + get_device(dev); + device_shutdown_tree(dev); + return 0; +} + +/* + * On shutdown each root device (the one that does not have a parent) goes + * through this function. + */ +static int device_shutdown_root_task(void *data) +{ + struct device *dev = (struct device *)data; + + device_shutdown_tree(dev); + + return 0; +} + /** * device_shutdown - call ->shutdown() on each device to shutdown. */ void device_shutdown(void) { - struct device *dev, *parent; + struct device *dev; - spin_lock(&devices_kset->list_lock); - /* - * Walk the devices list backward, shutting down each in turn. - * Beware that device unplug events may also start pulling - * devices offline, even as the system is shutting down. + /* Shutdown the root devices. The children are going to be + * shutdown first in device_shutdown_tree(). */ + spin_lock(&devices_kset->list_lock); while (!list_empty(&devices_kset->list)) { - dev = list_entry(devices_kset->list.prev, struct device, - kobj.entry); + dev = list_entry(devices_kset->list.next, struct device, + kobj.entry); - /* - * hold reference count of device's parent to - * prevent it from being freed because parent's - * lock is to be held - */ - parent = get_device(dev->parent); - get_device(dev); /* * Make sure the device is off the kset list, in the * event that dev->*->shutdown() doesn't remove it. */ list_del_init(&dev->kobj.entry); - spin_unlock(&devices_kset->list_lock); - /* hold lock to avoid race with probe/release */ - if (parent) - device_lock(parent); - device_lock(dev); - - device_shutdown_one(dev); - device_unlock(dev); - if (parent) - device_unlock(parent); - - put_device(parent); - - spin_lock(&devices_kset->list_lock); + /* Here we start tasks for root devices only */ + if (!dev->parent) { + /* Prevents devices from being freed. The counter is + * going to be decremented in device_shutdown_one() once + * this root device is shutdown. + */ + get_device(dev); + + /* We unlock list for performance reasons, + * dev->*->shutdown(), may try to take this lock to + * remove us from kset list. To avoid unlocking this + * list we could replace spin lock in: + * dev->kobj.kset->list_lock with a dummy one once + * device is locked in device_shutdown_root_task() and + * in device_shutdown_child_task(). + */ + spin_unlock(&devices_kset->list_lock); + + device_shutdown_root_task(dev); + spin_lock(&devices_kset->list_lock); + } } spin_unlock(&devices_kset->list_lock); } -- 2.17.0